一 java实现线程通信机制主要通过以下几种方式
1 while()轮询
2 等待/通知机制
wait()、notify()、notifyAll()结合synchronized关键字
3 条件对象:Condition
Condition和ReentrantLock重入锁
此外,java为线程间通信还提供了一些关键字:synchronized、volatile、final,这点我们最后在做介绍
第一种方式:while()轮询
第二种方式:等待/通知机制
synchronized是JVM提供的内置锁,我们通过java中的生产者消费者模式来看看wait/notify是如何实现线程间通信的
wait()/notify()/notifyAll()是Object类定义的方法,必须与synchronized一起使用,更确切的来说是在synchronized临界区内使用,
也就是说调用wait()方法的前提是该线程已经获取锁,调用notify()/notifyAll()的前提是也是该线程已经获取了锁,
wait() : 使当前线程进入阻塞状态,退出临界区,释放CPU,暂时放弃所获取的锁,当其他线程调用notify()/notidyAll()方法的时候,会唤醒一个或者多个处于等待该线程可能会被唤醒然后重新取竞争获取锁,如果重新获得了锁,被唤醒的线程会继续从上次阻塞的地方执行。
notify()/notifyAll():当前线程调用此方法,会唤醒之前阻塞的线程,但是notify()/notifyAll()方法不会释放锁,而是会继续往下执行,直到遇到wait()方法才会释放锁,所以一般线程调用完成notify()/notifyAll()方法后,都会调用wait()释放锁,退出临界区,从而让其他线程有机会获取锁
package com.demo.lock1;
import java.util.ArrayList;
import java.util.List;
public class ProducerConsumer {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
Producer producer = new Producer(list);
Consumer consumer = new Consumer(list);
producer.start();
consumer.start();
}
}
/**
* 生产者
*
* @author jiangry01
*
*/
class Producer extends Thread {
private List<Integer> list;// 仓库
public Producer(List<Integer> list) {
this.list = list;
}
public void run() {
while (true) {
synchronized (list) {
while (list.isEmpty()) {
for (int i = 0; i < 10; i++) {
list.add(i);
}
System.out.println("线程-" + Thread.currentThread().getName() + "-生产-" + list.size() + " :条消息");
}
list.notify();
//list.clear();
// try {
// list.wait();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
}
}
}
/**
* 消费者
*
* @author jiangry01
*
*/
class Consumer extends Thread {
private List<Integer> list;// 仓库
public Consumer(List<Integer> list) {
this.list = list;
}
public void run() {
while (true) {
synchronized (list) {
while (!list.isEmpty()) {
int size = list.size();
for (int i = size-1; i >= 0; i--) {
System.out.println(list.remove(i));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程-" + Thread.currentThread().getName() + "-仓库数据-" + list.size() + " :条消息");
}
list.notify();
// try {
// list.wait();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
}
}
}
第三钟方式:Condition和lock
借助于Condition对象,我们也可以实现类似于wait()/notify()机制的功能,ReentrantLock重入锁相当于synchronized,每个Lock可以有任意数量的Condition对象,线程可以注册指定的Condition对象上,从而可以有选择性的进行线程通知,实现线程多路通知,在线程调度上更具有灵活性,在Condiiton对象的await()方法相当于wait()方法,使线程放弃锁,退出临界区进入等待状态,singal()/singalAll()方法相当于notify()/notifyAll()方法,唤醒处于等待状态的线程。
package com.demo.lock1;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* 开启两个线程,一个线程向一个队列吐数据,当队列数据达到一定条数量的时候,通知另一个线程来消费数据
*
* @author jiangry01
*
*/
public class WriteAndRead {
public static int THRESHOLD = 5;
public static ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(5);
public static volatile int COUNT;// 队列的容量
public static ReentrantLock lock = new ReentrantLock();
public Condition condition1 = lock.newCondition();
public Condition condition2 = lock.newCondition();
public Condition condition3 = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
final WriteAndRead wr = new WriteAndRead();
new Thread() {
public void run() {
while (true)
wr.put();
}
}.start();
new Thread() {
public void run() {
while (true)
wr.get();
}
}.start();
}
// 写线程写入数据
public void put() {
// while (true){
System.out.println("-----------");
lock.lock();
try {
while (COUNT < THRESHOLD) {
// 如果消息队列的消息数量
System.out.println(Thread.currentThread().getName() + "写入: ");
queue.put(0);
COUNT = queue.size();
}
condition2.signal();
condition1.await();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getName() + "释放锁");
}
}
// }
// 读线程读取数据
public void get() {
// while (true){
lock.lock();
try {
while (COUNT > 0) {
// 如果消息队列的消息数量
int i = queue.poll();
System.out.println(Thread.currentThread().getName() + "读取: " + i);
COUNT = queue.size();
Thread.sleep(1000);
}
condition1.signal();
condition2.await();
System.out.println(queue.size());
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
// }
}