多线程间如何通信和共享数据,肯定好多小伙伴都不知道, 或者很久之前知道,现在已经忘却,现在我和大家一起复习一下。
一、线程间通信
1、线程间通信:A执行完,B才执行
/**
* 线程间通信:A执行完,B才执行
*/
@Test
public void bWaitA(){
Thread A = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("A 开始工作!!");
System.out.println("A 结束工作!!");
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("B 开始等待 A");
try {
A.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("B 开始工作!!");
System.out.println("B 结束工作!!");
}
});
B.start();
A.start();
}
2、A执行一部分,B执行完,A再执行剩下的部分
/**
* A执行一部分,B执行完,A再执行剩下的部分
* A 1, B 1, B 2, B 3, A 2, A 3
*
* 首先创建一个 A 和 B 共享的对象锁 lock = new Object();
* 当 A 得到锁后,先打印 1,然后调用 lock.wait() 方法,交出锁的控制权,进入 wait 状态;
* 对 B 而言,由于 A 最开始得到了锁,导致 B 无法执行;直到 A 调用 lock.wait() 释放控制权后, B 才得到了锁;
* B 在得到锁后打印 1, 2, 3;然后调用 lock.notify() 方法,唤醒正在 wait 的 A;
* A 被唤醒后,继续打印剩下的 2,3。
*/
@Test
public void aWaitB() {
Object lock = new Object();
Thread A = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("A 等待锁");
synchronized (lock) {
System.out.println("A 获得锁");
System.out.println("A 1");
try {
System.out.println("A wait 放弃锁控制权,等待再次获取锁控制权");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("被唤醒,重新获取锁控制权");
System.out.println("A 2");
System.out.println("A 3");
}
}
});
Thread B = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("B 等待获取锁控制权");
synchronized (lock) {
System.out.println("B 获取锁控制权");
System.out.println("B 1");
System.out.println("B 2");
System.out.println("B 3");
System.out.println("B 执行完毕 调用 notiry 方法");
lock.notify();
}
}
});
A.start();
B.start();
}
3、四个线程 A B C D,其中 D 要等到 A B C 全执行完毕后才执行,而且 A B C 是同步运行的
/**
* 四个线程 A B C D,其中 D 要等到 A B C 全执行完毕后才执行,而且 A B C 是同步运行的
*
* CountdownLatch 来实现这类通信方式。它的基本用法是:
* 创建一个计数器,设置初始值,CountdownLatch countDownLatch = new CountDownLatch(2);
* 在 等待线程 里调用 countDownLatch.await() 方法,进入等待状态,直到计数值变成 0;
* 在 其他线程 里,调用 countDownLatch.countDown() 方法,该方法会将计数值减小 1;
* 当 其他线程 的 countDown() 方法把计数值变成 0 时,等待线程 里的 countDownLatch.await() 立即退出,继续执行下面的代码。
*
* 执行过程:
* 其实简单点来说,CountDownLatch 就是一个倒计数器,我们把初始计数值设置为3,
* 当 D 运行时,先调用 countDownLatch.await() 检查计数器值是否为 0,若不为 0 则保持等待状态;
* 当A B C 各自运行完后都会利用countDownLatch.countDown(),将倒计数器减 1,
* 当三个都运行完后,计数器被减至 0;此时立即触发 D 的 await() 运行结束,继续向下执行。
* 因此,CountDownLatch 适用于一个线程去等待多个线程的情况。
*/
@Test
public void dWaitABC() {
int worker = 3;
CountDownLatch countDownLatch = new CountDownLatch(worker);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("D 等待其他线程执行完毕");
try {
countDownLatch.await();
System.out.println("所有需要等待的线程都执行完毕,D开始工作");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
for (char threadName='A'; threadName <= 'C'; threadName++) {
final String threadNameStr = String.valueOf(threadName);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(threadNameStr + " 正在工作");
System.out.println(threadNameStr + " 工作完成");
countDownLatch.countDown();
}
}).start();
}
}
4、个运动员各自准备,等到三个人都准备好后,再一起跑
/**
* 三个运动员各自准备,等到三个人都准备好后,再一起跑
*
* CyclicBarrier
* 为了实现线程间互相等待这种需求,我们可以利用 CyclicBarrier 数据结构,它的基本用法是:
* 先创建一个公共 CyclicBarrier 对象,设置 同时等待 的线程数,CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
* 这些线程同时开始自己做准备,自身准备完毕后,需要等待别人准备完毕,这时调用 cyclicBarrier.await(); 即可开始等待别人;
* 当指定的 同时等待 的线程数都调用了 cyclicBarrier.await();时,意味着这些线程都准备完毕好,然后这些线程才 同时继续执行。
*/
@Test
public void runABCWhenAllReady() {
int runner = 3;
CyclicBarrier cyclicBarrier = new CyclicBarrier(runner);
final Random random = new Random();
for (char runnerName='A'; runnerName <= 'C'; runnerName++) {
final String runnerNameStr = String.valueOf(runnerName);
new Thread(new Runnable() {
@Override
public void run() {
long prepareTime = random.nextInt(1000) + 100;
System.out.println(runnerNameStr + " 线程正在做准备 " );
try {
Thread.sleep(prepareTime);
} catch (Exception e) {
e.printStackTrace();
}
try {
System.out.println(runnerNameStr + " 线程准备完毕等待其他线程准备好");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("所有线程都准备好了,"+runnerNameStr +" 线程开始工作");
}
}).start();
}
// 让主线程休眠,让里面的线程执行完毕之前,主线程还存在
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
二、线程数据共享
/**
* 线程间数据共享
* 不同的线程,都有一个属性 ShareData ,并且线程的工作也封装 ShareData中。
* 不同线程中的run方法执行的都是 ShareData 中指定的封装的方法
*/
@Test
public void shareData(){
ShareData shareData = new ShareData();
for(int i=1;i<=2;i++){
new Thread(new MyIncreRunnable(shareData)).start();
new Thread(new MyDecreRunanble(shareData)).start();
}
}
/**
* 线程间数据共享
* 不同的线程,都有一个属性 ShareData ,并且线程的工作也封装 ShareData中。
* 不同线程中的run方法执行的都是 ShareData 中指定的封装的方法
*/
@Test
public void shareData(){
ShareData shareData = new ShareData();
for(int i=1;i<=2;i++){
new Thread(new MyIncreRunnable(shareData)).start();
new Thread(new MyDecreRunanble(shareData)).start();
}
}
class MyIncreRunnable implements Runnable{
private ShareData shareData;
public MyIncreRunnable(ShareData shareData) {
this.shareData = shareData;
}
@Override
public void run() {
for (int i = 1;i<=10;i++){
shareData.increment();
}
}
}
class MyDecreRunanble implements Runnable{
private ShareData shareData;
public MyDecreRunanble(ShareData shareData) {
this.shareData = shareData;
}
@Override
public void run() {
for(int i=1;i<=10;i++){
shareData.decrement();
}
}
}
/**
* 共享数据,对共享数据的操作也在这个对象中完成
*/
class ShareData{
private int count = 0;
public synchronized void increment(){
count++;
System.out.println(Thread.currentThread().getName()+" inc "+count);
}
public synchronized void decrement(){
count--;
System.out.println(Thread.currentThread().getName()+" dec " +count);
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/101707.html