线程间通信知识总结
为什么要进行线程间通信
多个线程并发执行时,在默认的情况下,CPU是随机切换线程的,而有时我们需要让CPU按照我们的期望来执行线程,此时就需要线程之间协调通信
线程间通信方式
1.休眠唤醒的方式,如:Object中的wait、notify、notifyAll和Condition中的await、signal、signalAll
2.CountDownLatch,用于某个线程A等待一个或多个其他线程执行完毕后,线程A才执行
3.CyclicBarrier,一组线程等待至某个状态后,再全部同时执行
4.Semaphore,用于控制对某组资源的访问权限
休眠唤醒方式
1.对于Object中的wait、notify方法使用的演示:
public class OddEven {
private int i = 0;//要打印的数
private Object o = new Object();
/**
* 打印奇数,由奇数线程来调用
*/
public void odd() {
while (i < 20) { //判断i是否小于20
//判断是否是奇数
synchronized (o) {
if (i % 2 == 1) {
System.out.println(Thread.currentThread().getName() + ": " + i++);
o.notify();//唤醒偶数线程打印
} else {
try {
o.wait(); //等待偶数线程打印完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 打印偶数,由偶数线程来调用
*/
public void even() {
while (i < 20) {
synchronized (o) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ": " + i++);
o.notify(); //唤醒奇数线程打印
} else {
try {
o.wait(); //等待奇数线程打印
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
OddEven oddEven = new OddEven();
Thread oddThread = new Thread(new Runnable() {
@Override
public void run() {
oddEven.odd();
}
}, "奇数线程");
Thread evenThread = new Thread(new Runnable() {
@Override
public void run() {
oddEven.even();
}
}, "偶数线程");
oddThread.start();
evenThread.start();
}
}
2.对于Condition中的await、signal方法使用的演示:
public class OddEven {
private int i = 0;//要打印的数
private Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
/**
* 打印奇数,由奇数线程来调用
*/
public void odd() {
while (i < 20) { //判断i是否小于20
//判断是否是奇数
lock.lock();
try {
if (i % 2 == 1) {
System.out.println(Thread.currentThread().getName() + ": " + i++);
condition.signal();//唤醒偶数线程打印
} else {
try {
condition.await(); //等待偶数线程打印完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
lock.unlock();
}
}
}
/**
* 打印偶数,由偶数线程来调用
*/
public void even() {
while (i < 20) {
lock.lock();
try {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ": " + i++);
condition.signal(); //唤醒奇数线程打印
} else {
try {
condition.await(); //等待奇数线程打印
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
OddEven oddEven = new OddEven();
Thread oddThread = new Thread(new Runnable() {
@Override
public void run() {
oddEven.odd();
}
}, "奇数线程");
Thread evenThread = new Thread(new Runnable() {
@Override
public void run() {
oddEven.even();
}
}, "偶数线程");
oddThread.start();
evenThread.start();
}
}
3.Object和Condition休眠唤醒使用总结:
①:object中的wait方法一定要配合synchronized(同步锁)使用
②:object中的wait方法需要用notify/notifyAll方法来唤醒
③:condition中的await方法一定要配合Lock(互斥锁/共享锁)来使用
④:condition中的await方法需要用signalAll方法来唤醒
CountDownLatch方式
CountDownLatch是在Java1.5中被引入的,存在于java.util.concurrent包下,通过一个计数器来实现,计数器的初始值为线程的数量,它能够使一个线程等待其他线程完成各自的工作后再执行。
关于CountDownLatch使用的代码演示:
/**
* 学生教师模型
* 教师需要等到所有学生都到齐并且准备就绪后才能开始教学任务
*/
public class CountDownLatchDemo {
//设置要等待的学生为3个
private CountDownLatch latch = new CountDownLatch(3);
/**
* 学生方法,由学生线程调用
*/
public void student() {
System.out.println(Thread.currentThread().getName() + "正在准备.......");
try {
//让线程睡眠1秒,表示学生的准备过程
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "准备就绪!");
latch.countDown();
}
/**
* 教师方法,由教师线程调用
*/
public void teacher() {
//1.获取教师线程名字
String name = Thread.currentThread().getName();
//2.教师线程等待所有的学生线程准备完毕,打印等待信息
System.out.println(name+"正在等待......");
//3.调用CountDownLatch的await方法等待学生线程
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//4.所有学生准备就绪,老师开始讲课
System.out.println("所有学生已经到位,"+name+"可以开始讲课!");
}
public static void main(String[] args) {
CountDownLatchDemo latchDemo = new CountDownLatchDemo();
//创建3个学生线程,调用学生方法
Thread s1 = new Thread(new Runnable() {
@Override
public void run() {
latchDemo.student();
}
},"一号学生");
Thread s2 = new Thread(new Runnable() {
@Override
public void run() {
latchDemo.student();
}
},"二号学生");
Thread s3 = new Thread(new Runnable() {
@Override
public void run() {
latchDemo.student();
}
},"三号学生");
//创建一个教师线程,调用教师方法
Thread teacher = new Thread(new Runnable() {
@Override
public void run() {
latchDemo.teacher();
}
},"老师");
s1.start();
s2.start();;
s3.start();
teacher.start();
}
}
CyclicBarrier方式
CyclicBarrier是在java1.5中被引入的,存在于java.util.concurrent包下,其底层是基于ReentrantLock和Condition实现的,可以让一组线程等待至某个状态之后再全部执行。
关于CyclicBarrier使用的代码演示:
public class CyclicBarrierDemo {
//构造方法中的参数表示参与CyclicBarrier的线程数
private CyclicBarrier barrier = new CyclicBarrier(3);
public void startThread(){
//1.准备启动
System.out.println(Thread.currentThread().getName()+"正在准备启动......");
try {
//2.调用Cyclicbarrier的await方法等待线程全部准备完成
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
//3.启动完毕
System.out.println(Thread.currentThread().getName()+"启动完毕 时间:"+new Date().getTime());
}
public static void main(String[] args) {
CyclicBarrierDemo barrierDemo = new CyclicBarrierDemo();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
barrierDemo.startThread();
}
},"线程1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
barrierDemo.startThread();
}
},"线程2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
barrierDemo.startThread();
}
},"线程3");
t1.start();
t2.start();
t3.start();
}
}
Semaphore方式
Semaphore是在java1.5中被引入的,存在于java.util.concurrent包下,用于控制线程对某组资源的访问权限
关于对Semaphore使用的代码演示:
具体场景:一次春游中,一共有6个孩子,但是只有2条单人小船可以供孩子们玩耍(即每条小船只能乘坐一人)
public class SemophoreDemo {
/**
* 孩子使用小船进行玩耍的过程
*/
static class Play implements Runnable {
private int childNum;//孩子的编号
private Semaphore semaphore;//小船数
public Play(int childNum, Semaphore semaphore) {
this.childNum = childNum;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
//1.孩子准备登上小船
semaphore.acquire();
//2.孩子已经登上小船,开始玩耍
System.out.println(Thread.currentThread().getName() + "登上小船,开始玩耍......");
//3.让线程睡眠1秒,模拟小孩坐在小船上玩耍的过程
TimeUnit.SECONDS.sleep(1);
//4.小孩玩够了,离开小船
semaphore.release();
System.out.println(Thread.currentThread().getName() + "玩够了,离开小船");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
int children = 6;
Semaphore semaphore = new Semaphore(2);
for (int i = 0; i < children; i++) {
new Thread(new Play(i,semaphore)).start();
}
}
}
总结
wait方法于sleep方法的区别:
多线程特性
1.原子性,即一个操作或者多个操作要么全部都执行并且在执行的过程中不会被打断,要么就不被执行
2.可见性,指的是当多个线程访问同一个变量时,其中的一个线程修改了这个变量的值,其他的线程能够立即看到被修改的值
3.有序性,即程序执行的顺序按照代码的先后顺序来执行
本文地址:https://blog.csdn.net/Zenrym/article/details/107867091