多线程通信(wait和notify)

线程通信概念:

线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体,线程间的通信就成为整体的必用方式之一。当线程存在通信指挥,系统间的交互性会更强大,在提高CPU利用率的同时还会使开发人员对线程任务在处理的过程中进行有效的把控与监督。

使用wait/notify方法实现线程间的通信。(注意这两个方法都是object的类的方法,换句话说java为所有的对象都提供了这两个方法)

1 wait和notify必须配合synchronized关键字使用

2wait方法释放锁,notify方法不释放锁

下面看一段demo:

public class ListAdd1 {
private volatile static List list = new ArrayList();

public void add() {
list.add("bjsxt");
}

public int size() {
return list.size();
}

public static void main(String[] args) {
final ListAdd1 list1 = new ListAdd1();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
list1.add();
System.out.println("当前线程: " + Thread.currentThread().getName()
+ "添加了一个元素。。");
Thread.sleep(500);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1");

Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (list1.size() == 5) {
System.out.println("当前线程收到通知 : " + Thread.currentThread().getName()
+ " list size = 5 线程停止");
throw new RuntimeException();
}
}
}
}, "t2");
t1.start();
t2.start();
}
}
首先看一下运行结果:

在第一个线程运行到list.size()=5的时候,停止t2线程,这种实现方式非常不好,需要t2线程一直去轮询。
可以使用java里面线程通信的方式去实现及重构上面的这种方式。下面我们看另外一个demo:
public class ListAdd2 {
private volatile static List list = new ArrayList();

public void add() {
list.add("bjsxt");
}

public int size() {
return list.size();
}

public static void main(String[] args) {
//实例化出来一个lock
//当使用 wait 和 notify 的时候,一定要配合着synchronized关键字去使用
final Object lock = new Object();
final ListAdd2 list2 = new ListAdd2();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
synchronized (lock) {
for (int i = 0; i < 10; i++) {
list2.add();
System.out.println("当前线程: " + Thread.currentThread().getName()
+ "添加了一个元素。。");
Thread.sleep(500);
if (list2.size() == 5) {
System.out.println("已经发出通知、、");
lock.notify();
}
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
if (list2.size() != 5) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("当前线程: " + Thread.currentThread().getName()
+ " 收到通知线程停止");
throw new RuntimeException();
}
}
}, "t2");
t2.start();
t1.start();
}
}

先看一下运行的结果:

他是先打印5个结果,然后发出通知,接着又执行5个元素,这个时候t2才接收到通知,说明这种方式是不具备实时性的,

看一下,这个线程是先启动t2的,首先是t2获取锁,然后等t2wait,这个时候,t2线程释放锁,此时,t1线程获得锁,

然后在list2.size()等于5的时候,调用notify方法,notify方法是不释放锁的,所以t2线程获取不到锁,这个时候,只能等待t1线程执行完,t2线程才能获取锁,进行业务处理。这也就是打印结果是这样的原因。

加入t1和t2换个位置的话,会造成t1获得锁,然后t2就不能获得锁,等t1执行完,list.size()直接就等于10了,然后才会运行t2线程。

弊端:一定要等待t1线程结束后,才能执行另外一个等待的线程,不具备实时性。

下面看一个解决这个弊端的demo:

public class ListAdd3 {
private volatile static List list = new ArrayList();

public void add() {
list.add("bjsxt");
}

public int size() {
return list.size();
}

public static void main(String[] args) {
final ListAdd3 list3 = new ListAdd3();
//括号里面是传递的个数
final CountDownLatch countDownLatch = new CountDownLatch(1);
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
list3.add();
System.out.println("当前线程: " + Thread.currentThread().getName()
+ "添加了一个元素。。");
Thread.sleep(500);
if (list3.size() == 5) {
System.out.println("已经发出通知、、");
countDownLatch.countDown();//类似于notify
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
if (list3.size() != 5) {
try {
countDownLatch.await();//类似于wait
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("当前线程: " + Thread.currentThread().getName()
+ " 收到通知线程停止");
throw new RuntimeException();
}

}, "t2");
t2.start();
t1.start();
}
}
看一下运行结果:

这就实现了线程通信间的实时性。括号里面的数字代表着下面的t1线程里面有几个countDown,具体相关的知识点,以后在分析。




原文地址:https://www.cnblogs.com/shmilyToHu/p/6394754.html