接触Java多线程这么久了,synchronized静态代码块以及其中的wait和notify方法一直没搞懂,网上关于此,不是旁敲侧击,就是晦涩难懂(我理解能力有限),实在无语了。今天把Java的源码导入到了eclipse,从源码中看到了我想要的答案。
即本文专注于讲解透彻地讲解synchronized代码块,wait以及notify的使用方法,绝对一针见血,绝不旁敲侧击,没意思。
注:本文不会讲什么是Java同步机制,以及为何要同步
我们开始吧。
一.抛出问题
1.notify和wait到底是属于线程,还是属于Object,它们的调用对线程又有着什么影响?
2.网上说notify和wait必须在synchronized代码块之内调用,为什么?
二.用Java源码解决问题
问题一:
我们来看源代码:Object的实现类
public class Object {
//这三个native方法是从Object源代码中找到的三个函数
public final native void notify();
public final native void notifyAll();
public final native void wait(long timeout) throws InterruptedException;
}
1.我们知道,Java中Object是一切类的父类。看到这里,有人说应该是子类继承了父类的这几个方法吧,但是我打开了String,Thread的实现类,搜索wait或者notify这个字符串竟然没搜到!说明了什么,说明这些方法属于Object,子类直接继承,根本就没做任何更改。即无论是哪个类的wait和notify,实现的效果都是相同的,只是我们现在还不知道具体是什么效果,所以我们只要搞懂了以上三个函数,一切都真相大白了。
2.有人说,上面不是三个抽象方法吗,什么都没实现啊,额,那你就需要查查native这个关键字是什么作用了。
下面我插一段来简单介绍一下native的意思:
简单地讲,一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。标识符native可以与所有其它的java标识符连用,但是abstract除外。这是合理的,因为native暗示这些方法是有实现体的,只不过这些实现体是非java的,但是abstract却显然的指明这些方法无实现体。
恩,是的,这些方法都已经实现了,只是不是用Java语言实现的,那我们是不是 对其用法就不得而知了?当然不是,Object用注释的方式给出了它们的具体用法,我们一起来看一下:(我会把重要的翻译一下,不要一掠而过,这是重点哦)
//先来看看对wait方法的注释:
Causes the current thread to wait until another thread invokes the java.lang.Object.notify() method or the java.lang.Object.notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0).
/**上一段话的意思是:一个Object的wait方法会导致当前线程等待,直到其
*他的线程调用同一个Object对象的notify方法或者notifyAll方法,
*换句话说,这个方法完全等同于wait(0);
*/
The current thread must own this object‘s monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object‘s monitor to wake up either through a call to the notify method or the notifyAll method.
/**
*上一段英文的意思是:当前线程必须拥有该Object的对象锁,
*调用wait后,交出对象锁的控制权,然后等待这个Object对象调用notify来唤醒它
*/
synchronized (obj) {
while (<condition does not hold>)
obj.wait();
... // Perform action appropriate to condition
}
This method should only be called by a thread that is the owner of this object‘s monitor. See the notify method for a description of the ways in which a thread can become the owner of a monitor.
/**
* 翻译:这段静态代码块只有获得了obj的对象锁之后的线程才能进行访问,
* 去查看notify方法的解释来了解一个线程如何获得一个对象对象锁
*/
//再来看看notify的注解:
Wakes up a single thread that is waiting on this object‘s monitor. If any threads are waiting on this object, one of them is chosen to be awakened. A thread waits on an object‘s monitor by calling one of the wait methods.
/**
* 唤醒一个正在等待该对象锁的线程。如果有正在等待的线程,
* 就随机唤醒一个,**一个线程通过调用一个Object的wait方法来停止运行等待这个Object的监视器(对象锁)(这句话是重点!!)**
*
*/
Only one thread at a time can own an object‘s monitor.
//一个对象的监视器在同一时间最多被一个线程获得
好了,我们通过对这些注解的阅读,一切一目了然了,我们来总结一下:
1.wait的作用:当前线程交出对象锁,并停止运行,等待再次获得这个对象锁(被哪个Object对象wait的,就必须得得到哪个Object对象的notify或者notifyAll方法才能运行)
2.notify的作用:随机唤醒一个线程,哪一个线程呢,就是被此Object对象wait的线程之一,即因调用了此Object对象而停止运行的线程。
1.notify和wait到底是属于线程,还是属于Object,它们的调用对线程又有着什么影响?
答:notify和wait属于Object,wait使线程停止运行并等待此对象的对象锁, notify,交出对象锁,并且唤醒一个正在等待的线程,一个正在等待该对象对象锁的线程。
问题二:
2.网上说notify和wait必须在synchronized代码块之内调用,为什么?
synchronized (obj) {
while (<condition does not hold>)
obj.wait();
... // Perform action appropriate to condition
}
This method should only be called by a thread that is the owner of this object‘s monitor.
答:因为只有获得对象锁的线程才有资格wait和notify。而进入这段代码块的肯定具备了obj的对象锁,否则也进不去啊。至于为啥,看看上面那句英文吧,翻译过了。
最后画一张图:
啊,尽力了。。。现在还不明白的那就多看几遍吧。我的表述能力只能这样了,当然,你听懂了,我很高兴。恩,就是这样。
最后,给个比较经典的多线程同步例子,大家看一下,如果能看懂,那就说明你真学明白了:(运行结果 ABCABCABC……)
public class JavaThread implements Runnable {
/**implements extend
* 实现线程的两种方式:
* 1.实现runnable接口
* 2.继承Thread类,实run方法:
*/
private String name;
private Object prev;
private Object self;
private JavaThread(String name, Object prev, Object self) {
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
int count = 10;
while (count > 0) {
synchronized (prev) {
synchronized (self) {
System.out.println(name);
count--;
self.notify();
}
try {
prev.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws Exception {
Object a = new Object();
Object b = new Object();
Object c = new Object();
JavaThread pa = new JavaThread("A", c, a);
JavaThread pb = new JavaThread("B", a, b);
JavaThread pc = new JavaThread("C", b, c);
new Thread(pa).start();
Thread.sleep(100);
new Thread(pb).start();
Thread.sleep(100);
new Thread(pc).start();
}
}
有问题请留言喽。