一:线程间的通信
1.1:wait/notify机制的原理
1.wait方法的作用是使当前执行的wait方法的线程等待,并释放锁,直到接到通知或被中断为止
2.拥有相同锁的线程才可以实现wait/notify机制
3.wait和notify方法必须在同步中使用,否则会发生IllegalMonotorStateException异常
4.notify方法是通知等待该锁的其他线程!,所以前提是wait方法和notify方法使用的是同一把锁
5.notify方法执行后,并不会立即释放锁,需要执行完notify所在的同步区域后,当前线程才会释放锁
6.如果发出notify操作时,没有在锁中等待的线程,则该命令被忽略
7.一般都是使用锁对象来调用wait/notify方法
1.2:实现wait/notify机制
- 例子1
/**
* 完整实现wait/notify机制
*/
public class Demo {
public static void main(String[] args) {
Object lock = new Object();
new Thread(()->{
try {
synchronized (lock){
System.out.println(Thread.currentThread().getName()+" :wait start");
lock.wait();
System.out.println(Thread.currentThread().getName()+" :wait end");
}
}catch (InterruptedException e){
e.printStackTrace();
}
},"兔子").start();
try {
//让主线程休眠2s, 让兔子线程充分的先执行
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() ->{
synchronized (lock){
System.out.println(Thread.currentThread().getName()+" :notify start");
lock.notify();
System.out.println(Thread.currentThread().getName()+" :notify end");
}
},"乌龟").start();
}
}
- 控制台
兔子 :wait start
乌龟 :notify start
乌龟 :notify end
兔子 :wait end
分析:
1.为什么还加了个Thread.sleep(2000);
目的就是让兔子线程先执行,为什么要让兔子线程先执行呢?,你想想如果不加Thread.sleep(2000),
兔子线程和乌龟线程几乎同时启动线程,有几率cpu的执行权被乌龟线程抢走了,那么这时执行如下:
乌龟 :notify start
乌龟 :notify end
兔子 :wait start
注:兔子线程还在等待!
2.注意notify执行后,并没有立即释放锁,需要执行完notify所在的同步区域后,当前线程才会释放锁
所以等到打印出: 乌龟 :notify end,才会去执行 兔子 :wait start
注意,wait/notify方法必须使用同一把锁!
- 例子2
public class Demo {
private static List<String> list = new ArrayList<>();
private static Object lock = new Object();
public static void main(String[] args) {
new Thread(()->{
try {
synchronized (lock){
if(list.size()!=5){
System.out.println("wait start...");
lock.wait();
System.out.println("wait end...");
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}).start();
try {
//让主线程休眠2s, 让上面的线程先处于阻塞状态!
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() ->{
try {
synchronized (lock){
for (int i = 1; i <= 10; i++) {
list.add("hello world");
if(list.size() == 5){
lock.notify();
System.out.println("已经发出通知");
}
System.out.println("添加了"+i+"个元素");
Thread.sleep(1000);
}
}
}catch (Exception e){
e.printStackTrace();
}
}).start();
}
}
- 控制台
wait start...
添加了1个元素
添加了2个元素
添加了3个元素
添加了4个元素
已经发出通知
添加了5个元素
添加了6个元素
添加了7个元素
添加了8个元素
添加了9个元素
添加了10个元素
wait end...
注:通过控制台可以看出,notify方法执行完后并没有马上释放锁,而是执行完所在的同步区域的代码后,才释放的锁。
- 例子3:业务代码要尽量放在Service业务类中进行处理,这样更加标准
1、业务类
//业务类
public class Service {
private Object lock = new Object();
private List<String> list = new ArrayList<>();
public void waitMethod(){
try {
synchronized (lock){
if(list.size() != 5){
System.out.println("waitMethod start...");
lock.wait(); //释放锁
System.out.println("waitMethod end...");
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
public void notifyMethod(){
try {
synchronized (lock){
for (int i = 1; i <= 10; i++) {
list.add("hello world");
if(list.size() == 5){
lock.notify();
System.out.println("仅仅是发出通知,并没有立即释放锁,需要待notify所在的同步区域执行完后,才释放锁.");
}
System.out.println("添加了"+i+"个元素");
Thread.sleep(1000);//不释放锁
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
2、线程类
public class ThreadA extends Thread{
private Service service;
public ThreadA(Service service){
this.service = service;
}
@Override
public void run() {
service.waitMethod();
}
}
public class ThreadB extends Thread{
private Service service;
public ThreadB(Service service){
this.service = service;
}
@Override
public void run() {
service.notifyMethod();
}
}
3、测试类
/**
* 完整实现wait/notify机制
*/
public class Demo {
public static void main(String[] args) {
Service service = new Service();
ThreadA threadA = new ThreadA(service);
threadA.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ThreadB threadB = new ThreadB(service);
threadB.start();
}
}
- 控制台
waitMethod start...
添加了1个元素
添加了2个元素
添加了3个元素
添加了4个元素
仅仅是发出通知,并没有立即释放锁,需要待notify所在的同步区域执行完后,才释放锁.
添加了5个元素
添加了6个元素
添加了7个元素
添加了8个元素
添加了9个元素
添加了10个元素
waitMethod end...
- 总结
创建线程的时候,将业务对象作为构造参数传递进来,执行业务类中的方法即可。
注:来自虽然,但是菜的cxy