线程间协作
一、线程间协作:线程间的协作是为了解决线程执行时谁先谁后的为题,可以合理的确定多线程中线程的先后执行顺序
二、线程协作使用的方法:谁调用了wait方法,谁就必须调用notify或notifyAll方法,并且“谁”是对象锁
wait():对象锁调用了wait()方法,会使持有该对象锁的对象处于阻塞状态并释放对象锁的控制权,直到其他线程中的对象锁调用notify()方法或notifyAll()方法时等待此对象锁的线程才会被唤醒
notify():对象锁调用notify()方法就会唤醒在此对象锁上等待的单个线程
notifyAll():对象锁调用notifyAll()方法就会唤醒在此对象锁上等待的所有线程,只是线程结束了阻塞状态,进入了就绪状态,需要同其他的线程重新竞争CPU的执行权
三、使用Object类中的wait()、notify()和notifyAll()三个方法需要注意以下几点:
1.wait()方法需要和notify()或notifyAll()方法中的一个配对使用,且wait方法与notify()或notifyAll()方法配对使用时不能在同一个线程中
class CounterThread extends Thread {
private Object lockObj;
public CounterThread(Object lockObj) {
this.lockObj = lockObj;
}
@Override
public void run() {
synchronized (lockObj) {
try {
System.out.println("wait方法被执行!");
lockObj.wait();//执行该方法的线程进入阻塞,因此对此程序而言,下面代码永远不会被执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (lockObj) {
System.out.println("notify方法被执行");
lockObj.notify();
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
Object lockObj = new Object();
new CounterThread(lockObj).start();
}
}
2.wait()方法、notify()方法和notifyAll()方法必须在同步方法或者同步代码块中使用,否则出现IllegalMonitorStateException 异常
3.调用wait()方法、notify()方法和notifyAll()方法的对象必须和同步锁对象是一个对象
public class Watch {
String currentTime;
DisplayThread displayThread = new DisplayThread();
public static void main(String[] args) {
new Watch().displayThread.start();
}
/**
* 该线程负责显示时间
*/
class DisplayThread extends Thread{
@Override
public synchronized void run() {//相当于synchronized(this),此时的this代表DisplayThread对象,与TimeThread对象不是同一个对象,不能实现线程间的协作
new TimeThread().start();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(currentTime);
}
}
/**
* 该线程负责获取时间
*/
class TimeThread extends Thread{
@Override
public synchronized void run() {//相当于synchronized(this),此时的this代表TimeThread对象,与DisplayThread对象不是同一个对象,不能实现线程间的协作
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String pattern = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
currentTime = sdf.format(new Date());
this.notify();
}
}
}
四、sleep()方法和wait()方法区别
执行sleep()方法的线程会进入阻塞,但它不会执行释放对象锁,其他的线程依旧无法执行临界区中的代码
public class Test {
public static void main(String[] args) {
Object lockObj = new Object();
new PrintThread("1号打印机",lockObj).start();
new PrintThread("2号打印机",lockObj).start();
}
}
class PrintThread extends Thread {
private Object lockObj;
public PrintThread(String threadName, Object lockObj) {
super(threadName);
this.lockObj = lockObj;
}
@Override
public void run() {
synchronized (lockObj) {//当某台打印机执行临界区中的代码,输出线程名后由于调用了sleep方法,从而使得该打印机线程阻塞30秒,在这30秒期间因该打印机线程仍然持有对象锁,从而导致另一台打印机线程只能在30秒后才能执行临界区中的代码。
System.out.println(getName()+":"+new Date());
try {
sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//打印结果:
//1号打印机:Sat Aug 24 17:56:25 CST 2019
//2号打印机:Sat Aug 24 17:56:55 CST 2019
//确实如我们所想,由于调用了sleep方法,从而使得该打印机线程阻塞30秒,在这30秒期间因该打印机线程仍然持有对象锁,从而导致另一台打印机线程只能在30秒后才能执行临界区中的代码。
执行wait()方法的线程会进入阻塞的状态,同时释放对象锁,其他线程可以执行临界区域中的代码
public class Test {
public static void main(String[] args) {
Object lockObj = new Object();
new PrintThread("1号打印机",lockObj).start();
new PrintThread("2号打印机",lockObj).start();
}
}
class PrintThread extends Thread {
private Object lockObj;
public PrintThread(String threadName, Object lockObj) {
super(threadName);
this.lockObj = lockObj;
}
@Override
public void run() {
synchronized (lockObj) {
System.out.println(getName());
try {
lockObj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//打印结果:
//1号打印机Sat Aug 24 18:00:32 CST 2019
//2号打印机Sat Aug 24 18:00:32 CST 2019
//证明在对象锁执行wait方法时,会释放对象锁的控制权,其他线程也可以执行临界区中的代码
五、wait()方法
class CounterThread extends Thread {
private Object lockObj;
public CounterThread(String threadName, Object lockObj) {
super(threadName);
this.lockObj = lockObj;
}
@Override
public void run() {
int i = 1;
while (true) {
synchronized (lockObj) {
System.out.println(getName() + ":" + i);
try {
lockObj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}
}
}
public class Test {
public static void main(String[] args) {
Object lockObj = new Object();
new CounterThread("1号计数器",lockObj).start();
new CounterThread("2号计数器",lockObj).start();
}
}
//尽管此处是死循环,但由于对象锁lockObj调用了wait()方法,使得分别持有该lockObj对象锁的“1号计数器”线程和“2号计数器”线程处于线程等待状态,所以循环并没有继续下去
六、notify()方法
public class Watch {
String currentTime;
DisplayThread displayThread = new DisplayThread();
private static final Object lockObject = new Object();
public static void main(String[] args) {
new Watch().displayThread.start();
}
/**
* 该线程负责显示时间
*/
class DisplayThread extends Thread{
@Override
public void run() {
synchronized (lockObject) {
new TimeThread().start();
try {
lockObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(currentTime);//输出结果一定不为null
}
}
}
/**
* 该线程负责获取时间
*/
class TimeThread extends Thread{
@Override
public void run() {
synchronized (lockObject) {
try {
sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String pattern = "yyyy-MM-dd HH:mm:ss";
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
currentTime = sdf.format(new Date());
lockObject.notify();
}
}
}
}
七、notifyAll()方法
class CounterThread extends Thread {
private Object lockObj;
public CounterThread(String threadName, Object lockObj) {
super(threadName);
this.lockObj = lockObj;
}
@Override
public void run() {
int i = 1;
while (true) {
synchronized (lockObj) {
System.out.println(getName() + ":" + i);
try {
lockObj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
}
}
}
class NotifyAllThread extends Thread {
private Object lockObj;
public NotifyAllThread(Object lockObj) {
this.lockObj = lockObj;
}
@Override
public void run() {
synchronized (lockObj) {
System.out.println("notifyAll方法执行完毕");
lockObj.notifyAll();
}
}
}
public class Test {
public static void main(String[] args) {
Object lockObj = new Object();
new CounterThread("1号计数器", lockObj).start();
new CounterThread("2号计数器", lockObj).start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new NotifyAllThread(lockObj).start();
}
}