Data.java
package org.example.testwaitnotify;
public class Data {
private int value;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
ThreadA.java
package org.example.testwaitnotify;
import java.util.concurrent.TimeUnit;
public class ThreadA extends Thread {
private Data data;
private Object lock;
public ThreadA(Data data, Object lock) {
this.data = data;
this.lock = lock;
}
@Override
public void run() {
System.out.println("A start");
synchronized (lock) {
System.out.println("A Enter");
try {
// 模拟线程干活需要2s得到结果
TimeUnit.SECONDS.sleep(2);
data.setValue(666);
// 得到结果后就通知等待的那个线程
lock.notify();
// 然而通知后,另外那个线程无法被cpu调度,因为这个线程没有退出synchronized块,还没释放锁
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Main.java
package org.example.testwaitnotify;
public class Main {
public static void main(String[] args) {
Data data = new Data();
Object lock = new Object();
new ThreadA(data, lock).start();
System.out.println("Main start");
synchronized (lock) {
try {
// wait后线程进入WAITING状态。
// 释放cpu执行权 + 释放锁
// 没退出synchronized也要释放锁,不释放别的线程也没办法干活了呀!
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main read val=" + data.getValue());
}
}
}
/*
Main start
A start
A Enter
Main read val=666
*/
理解:
在Main等着的时候,ThreadA即使notify了,只是说:Main处于就绪状态了,依然无法立刻获得锁。
依然要等到ThreadA执行完,退出synchronized后,才会释放锁,Main才会获得锁接着执行。
思考:
wait后是线程进入WAITING状态。 别的线程notify它后,应该是进入等待被cpu调度的状态,其实就是RUNNABLE状态,注意这个包含有就绪和正在运行的线程 (NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED)
==================例子:N个线程交替指定0~M个数字================
package org.example.testcrossprint;
public class Main {
/**
* 当前打印到的数字
*/
private static volatile int curNum = 0;
/**
* 打印到的最大数字
*/
private static final int MAX_NUM = 100;
/**
* 线程数
*/
private static final int THREAD_NUM = 10;
/**
* 共享的锁
*/
private static final Object lock = new Object();
public static void main(String[] args) {
for (int i = 1; i <= THREAD_NUM; i++) {
new Thread(new Turn(i), "线程" + i).start();
}
}
private static class Turn implements Runnable {
private int index;
public Turn(int index) {
this.index = index;
}
@Override
public void run() {
while (curNum <= MAX_NUM) {
synchronized (lock) {
if ((curNum % THREAD_NUM + 1) != index) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println(Thread.currentThread().getName() + ":" + curNum++);
lock.notifyAll();
}
}
}
}
}
}
/*
线程1:0
线程2:1
线程3:2
线程4:3
线程5:4
线程6:5
线程7:6
线程8:7
线程9:8
线程10:9
线程1:10
线程2:11
线程3:12
线程4:13
线程5:14
线程6:15
线程7:16
线程8:17
线程9:18
线程10:19
线程1:20
线程2:21
线程3:22
线程4:23
线程5:24
线程6:25
线程7:26
线程8:27
线程9:28
线程10:29
线程1:30
线程2:31
线程3:32
线程4:33
线程5:34
线程6:35
线程7:36
线程8:37
线程9:38
线程10:39
线程1:40
线程2:41
线程3:42
线程4:43
线程5:44
线程6:45
线程7:46
线程8:47
线程9:48
线程10:49
线程1:50
线程2:51
线程3:52
线程4:53
线程5:54
线程6:55
线程7:56
线程8:57
线程9:58
线程10:59
线程1:60
线程2:61
线程3:62
线程4:63
线程5:64
线程6:65
线程7:66
线程8:67
线程9:68
线程10:69
线程1:70
线程2:71
线程3:72
线程4:73
线程5:74
线程6:75
线程7:76
线程8:77
线程9:78
线程10:79
线程1:80
线程2:81
线程3:82
线程4:83
线程5:84
线程6:85
线程7:86
线程8:87
线程9:88
线程10:89
线程1:90
线程2:91
线程3:92
线程4:93
线程5:94
线程6:95
线程7:96
线程8:97
线程9:98
线程10:99
线程1:100
*/
轻松的写出来了,可见自己已经理解了wait和notify了。 而且这里必须使用notifyAll才行,不然notify是随机唤醒一个,它不满足条件就会一直wait下去,程序就无法运行下去了。
笔记:
这次对内部类有了一个认识,之前总喜欢把类都单独定义,这样子让每个类短一点,这是没错的。 static 内部类的好处是:可以共享主类的一个普通变量。如:这里的计数。
如果单独定义一个Data类对象作为参数传递给每个线程,即使加了volatile变量,但是可见性未必行。应该 是 AtomicInteger这种应该是可以的。