惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

腾讯CDC
Microsoft Azure Blog
Microsoft Azure Blog
L
LangChain Blog
Y
Y Combinator Blog
Microsoft Security Blog
Microsoft Security Blog
宝玉的分享
宝玉的分享
B
Blog RSS Feed
MongoDB | Blog
MongoDB | Blog
Jina AI
Jina AI
D
Docker
B
Blog
Engineering at Meta
Engineering at Meta
Last Week in AI
Last Week in AI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
I
InfoQ
G
Google Developers Blog
博客园 - Franky
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The GitHub Blog
The GitHub Blog
T
The Blog of Author Tim Ferriss
大猫的无限游戏
大猫的无限游戏
阮一峰的网络日志
阮一峰的网络日志
U
Unit 42

博客园 - Neonuu

解决ollama官方下载过慢的问题(包含windows和linux) 使用java 命令运行包含main方法的class文件时,报 Error: Could not find or load main class Test Java8 list.stream()常用方法 IDEA整合SVN VUE,编辑详情时,列表数据也跟着改变 VUE二级联动,改变一级下拉框,清空二级下拉框 MySQL面试题 JVM原理 Linux服务器安装MariaDB数据库 jar包解压后,修改完配置文件,再还原成jar包 Java获取resources文件夹下properties配置文件 Java中Runnable和Thread的区别(网上部分说法是错误的) Java中如何实现一个接口拥有多个实现类 JPA中getOne与findOne try catch与spring的事务回滚 Spring主动触发事务回滚 Redis五种数据类型及应用场景 Java面试题 SQL HAVING用法详解 const,var,let区别
Java中synchronized,wait(),notify()
Neonuu · 2021-08-30 · via 博客园 - Neonuu

在JAVA中,是没有类似于PV操作、进程互斥等相关的方法的。JAVA的进程同步是通过synchronized()来实现的,需要说明的是,JAVA的synchronized()方法类似于操作系统概念中的互斥内存块,在JAVA中的Object类型中,都是带有一个内存锁的,在有线程获取该内存锁后,其它线程无法访问该内存,从而实现JAVA中简单的同步、互斥操作。明白这个原理,就能理解为什么synchronized(this)与synchronized(static XXX)的区别了,synchronized就是针对内存区块申请内存锁,this关键字代表类的一个对象,所以其内存锁是针对相同对象的互斥操作,而static成员属于类专有,其内存空间为该类所有成员共有,这就导致synchronized()对static成员加锁,相当于对类加锁,也就是在该类的所有成员间实现互斥,在同一时间只有一个线程可访问该类的实例。如果只是简单的想要实现在JAVA中的线程互斥,明白这些基本就已经够了。但如果需要在线程间相互唤醒的话就需要借助Object.wait(), Object.nofity()了。

    Obj.wait(),与Obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,与notify是针对已经获取了Obj锁进行操作,从语法角度来说就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内。从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。相应的notify()就是对对象锁的唤醒操作。但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操作。Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。

单单在概念上理解清楚了还不够,需要在实际的例子中进行测试才能更好的理解。对Object.wait(),Object.notify()的应用最经典的例子,应该是两个线程交替打印的问题了:

package com.neonuu.collection.domain;

public class ThreadTest2 {
    static class TestThread implements Runnable {
        int i = 1;

        @Override

        public void run() {
            while (true) {
                /*指代的为TestThread,因为使用的是implements方式。若使用继承Thread类的方式,慎用this*/

                synchronized (this) {
                    /*唤醒另外一个线程,注意是this的方法,而不是Thread*/

                    notify();

                    try {
                        /*使其休眠100毫秒,放大线程差异*/

                        Thread.currentThread();

                        Thread.sleep(100);

                    } catch (InterruptedException e) {
                        e.printStackTrace();

                    }

                    if (i <= 10) {
                        System.out.println(Thread.currentThread().getName() + ":" + i);

                        i++;

                        try {
                            /*放弃资源,等待*/

                            wait();

                        } catch (InterruptedException e) {
                            e.printStackTrace();

                        }

                    }else {
                        System.out.println(Thread.currentThread().getName() +"结束");
                        break;

                    }

                }

            }

        }

    }

    public static void main(String[] args) {
        /*只有一个TestThread对象*/

        TestThread t = new TestThread();

        Thread t1 = new Thread(t);

        Thread t2 = new Thread(t);

        t1.setName("线程1");

        t2.setName("线程2");

        t1.start();

        t2.start();
    }
}

结果:

线程1:1
线程2:2
线程1:3
线程2:4
线程1:5
线程2:6
线程1:7
线程2:8
线程1:9
线程2:10
线程1结束
线程2结束