简析synchronized原理与Java对象中的等待通知机制实现原理

什么是并发编程中的等待通知机制?简而言之就是一个线程修改了一个对象的值,而另一个线程感知到了变化,然后进行相应的操作。在前面的博客Java JUC 并发包组件之LockSupport与Condition的使用中,我们介绍了使用队列同步器实现的等待通知机制,本篇我们介绍使用synchronized实现的Java对象的等待通知机制,等待/通知的相关方法是任意Java对象都具备的,因为这些方法被定义在所有对象的超类 java.lang.Object上,方法和描述如下表所示:

方法名

描述

wait()

调用该方法的线程进入WAITING状态,只有等待另外线程的通知或者被中断才会返回,需要注意,调用wait方法后会释放对象的锁

wait(long)

等待一段之间,如果没有通知就超时返回。参数为毫秒

wait(long, int)

对超时时间更加细粒度的控制,可以达到纳秒

notify()

通知一个在对象上等待的线程使其从wait方法返回,而返回的前提是该线程获取到了对象的锁

notifyAll()

通知所有等待在该对象上的线程

如上方法:一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B 调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而执行后续操作,这就是Java对象的等待通知机制,这些方法需要配合synchronized使用,首先我们先介绍synchronized的实现原理,关键字synchronized可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程 在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性。

前面介绍synchronized的使用时已经介绍过任意一个对象都拥有自己的监视器,当这个对象由同步块或者这个对象的同步方法调用 时,执行方法的线程必须先获取到该对象的监视器才能进入同步块或者同步方法,而没有获 取到监视器(执行该方法)的线程将会被阻塞在同步块和同步方法的入口处,进入BLOCKED 状态,如下图描述了对象、对象的监视器、同步队列和执行线程之间的关系:

 任意线程对Object(Object由synchronized保护)的访问,首先要获得 Object的监视器。如果获取失败,线程进入同步队列,线程状态变为BLOCKED。当访问Object 的前驱(获得了锁的线程)释放了锁,则该释放操作唤醒阻塞在同步队列中的线程,使其重新 尝试对监视器的获取。这里不再介绍synchronized的使用,可以参考博客线程的安全性与synchronized的使用与锁升级

前面我们介绍了synchronized的内部实现,下面我们介绍Java对象的等待通知机制的实现,相对于synchronized的实现,等待通知机制是在synchronized实现的基础上实现的,流程更加复杂而已。我们先编写一个等待通知的示例,代码如下:

public class WaitNotify {
    static boolean flag = true;
    static Object lock = new Object();
    public static void main(String[] args) throws Exception {
	//等待线程
	Thread waitThread = new Thread(new Wait(), "WaitThread");
	waitThread.start();
	TimeUnit.SECONDS.sleep(1);
	//通知线程
	Thread notifyThread = new Thread(new Notify(), "NotifyThread");
	notifyThread.start();
    }

    static class Wait implements Runnable {
        public void run() {
	    // 加锁,拥有lock的Monitor
	    synchronized (lock) { 
		// 当条件不满足时,继续wait,同时释放了lock的锁
		while (flag) {
		    try {
		        System.out.println("WAITING 线程启动并且执行");
		        //调用wait方法,线程进入WAITING状态
			lock.wait();
		    } catch (InterruptedException e) {
					}
		} // 条件满足时,完成工作
            }
            System.out.println("收到 notify通知,获取对象监视器,继续执行");
        }
    }

    static class Notify implements Runnable {
	public void run() {
	    // 加锁,拥有lock的Monitor
	    synchronized (lock) { 
            // 获取lock的锁,然后进行通知,通知时不会释放lock的锁, 
	    System.out.println("通知线程启动");
	    //通知其他处理WAITING状态的线程
	    lock.notifyAll();
	    flag = false;
	    }
	}
    }
}

从上面的例子可知Java对象的等待/通知机制依托于同步机制,其目的就是确保等待线程从 wait()方法返回时能够感知到通知线程对变量做出的修改。在上面的示例中WaitThread首先获取了对象的锁,然后调用对象的wait()方法,从而放弃了锁 并进入了对象的等待队列WaitQueue中,进入等待状态。由于WaitThread释放了对象的锁,NotifyThread随后获取了对象的锁,并调用对象的notify()方法,将WaitThread从WaitQueue移到 SynchronizedQueue中,此时WaitThread的状态变为阻塞状态。NotifyThread释放了锁之后, WaitThread再次获取到锁并从wait()方法返回继续执行。 如下为其状态转换流程图:

猜你喜欢

转载自blog.csdn.net/wk19920726/article/details/108710059
今日推荐