JUC高并发编程
三、线程间通信
线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的。
3.1)线程间通信案例
两个线程,一个线程对当前数值加1,另一个线程对当前数值减1,要求用线程间通信
3.1.1)synchronized 方案
package com.study.sync;
?
//第一步 创建资源类,定义属性和操作方法
class Share {
? ?//初始值
? ?private int number = 0;
? ?//+1的方法
? ?public synchronized void incr() throws InterruptedException {
? ? ? ?//第二步 判断 干活 通知
? ? ? ?//判断:number值是否是0,如果不是0,等待
? ? ? ?// if (number != 0) { --> 会产生虚假唤醒问题,即只对第一次进入的条件进行了判断,使用while判断可以避免虚假唤醒
? ? ? ?while (number != 0) {
? ? ? ? ? ?//在哪里睡,就在哪里醒
? ? ? ? ? ?this.wait();
? ? ? }
? ? ? ?//干活:如果number值是0,就+1操作
? ? ? ?number++;
? ? ? ?System.out.println("当前线程名:" + Thread.currentThread().getName() + " :: " + number);
? ? ? ?//通知:通知其他线程
? ? ? ?this.notifyAll();
? }
?
? ?//-1的方法
? ?public synchronized void decr() throws InterruptedException {
? ? ? ?//判断:number值是否是1,如果不是1,等待
? ? ? ?// if (number != 1) { --> 会产生虚假唤醒问题,即只对第一次进入的条件进行了判断,使用while判断可以避免虚假唤醒
? ? ? ?while (number != 1) {
? ? ? ? ? ?this.wait();
? ? ? }
? ? ? ?//干活:如果number值是1,就-1操作
? ? ? ?number--;
? ? ? ?System.out.println("当前线程名:" + Thread.currentThread().getName() + " :: " + number);
? ? ? ?//通知:通知其他线程
? ? ? ?this.notifyAll();
? }
}
?
public class ThreadDemo1 {
? ?//第三步 创建多个线程,调用资源类的操作方法
? ?public static void main(String[] args) {
? ? ? ?Share share = new Share();
? ? ? ?//创建线程
? ? ? ?// 创建线程 AA
? ? ? ?new Thread(() -> {
? ? ? ? ? ?for (int i = 1; i <= 10; i++) {
? ? ? ? ? ? ? ?try {
? ? ? ? ? ? ? ? ? ?share.incr(); //+1
? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? }, "AA").start();
? ? ? ?// 创建线程 BB
? ? ? ?new Thread(() -> {
? ? ? ? ? ?for (int i = 1; i <= 10; i++) {
? ? ? ? ? ? ? ?try {
? ? ? ? ? ? ? ? ? ?share.decr(); //-1
? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? }, "BB").start();
? ? ? ?// 创建线程 CC
? ? ? ?new Thread(() -> {
? ? ? ? ? ?for (int i = 1; i <= 10; i++) {
? ? ? ? ? ? ? ?try {
? ? ? ? ? ? ? ? ? ?share.incr(); //+1
? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? }, "CC").start();
? ? ? ?// 创建线程 DD
? ? ? ?new Thread(() -> {
? ? ? ? ? ?for (int i = 1; i <= 10; i++) {
? ? ? ? ? ? ? ?try {
? ? ? ? ? ? ? ? ? ?share.decr(); //-1
? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? }, "DD").start();
? }
}
输出:
com.study.sync.ThreadDemo1 当前线程名:AA :: 1 当前线程名:BB :: 0 当前线程名:AA :: 1 当前线程名:BB :: 0 当前线程名:AA :: 1 当前线程名:BB :: 0 当前线程名:AA :: 1 当前线程名:BB :: 0 当前线程名:AA :: 1 当前线程名:BB :: 0 当前线程名:CC :: 1 当前线程名:BB :: 0 当前线程名:AA :: 1 当前线程名:BB :: 0 当前线程名:CC :: 1 当前线程名:BB :: 0 当前线程名:AA :: 1 当前线程名:DD :: 0 当前线程名:CC :: 1 当前线程名:BB :: 0 当前线程名:AA :: 1 当前线程名:DD :: 0 当前线程名:CC :: 1 当前线程名:BB :: 0 当前线程名:AA :: 1 当前线程名:DD :: 0 当前线程名:CC :: 1 当前线程名:DD :: 0 当前线程名:AA :: 1 当前线程名:DD :: 0 当前线程名:CC :: 1 当前线程名:DD :: 0 当前线程名:CC :: 1 当前线程名:DD :: 0 当前线程名:CC :: 1 当前线程名:DD :: 0 当前线程名:CC :: 1 当前线程名:DD :: 0 当前线程名:CC :: 1 当前线程名:DD :: 0
3.1.2)Lock 方案
package com.study.lock;
?
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
?
//第一步 创建资源类,定义属性和操作方法
class Share {
? ?private int number = 0;
?
? ?//创建Lock
? ?private Lock lock = new ReentrantLock();
? ?private Condition condition = lock.newCondition();
?
? ?//+1 方法
? ?public void incr() throws InterruptedException {
? ? ? ?//上锁
? ? ? ?lock.lock();
? ? ? ?try {
? ? ? ? ? ?//判断:number值是否是0,如果不是0,等待
? ? ? ? ? ?// if (number != 0) { --> 会产生虚假唤醒问题,即只对第一次进入的条件进行了判断,使用while判断可以避免虚假唤醒
? ? ? ? ? ?while (number != 0) {
? ? ? ? ? ? ? ?condition.await();
? ? ? ? ? }
? ? ? ? ? ?//干活:如果number值是0,就+1操作
? ? ? ? ? ?number++;
? ? ? ? ? ?System.out.println("当前线程名:" + Thread.currentThread().getName() + " :: " + number);
? ? ? ? ? ?//通知:通知其他线程
? ? ? ? ? ?condition.signalAll();
? ? ? } finally {
? ? ? ? ? ?//解锁
? ? ? ? ? ?lock.unlock();
? ? ? }
? }
?
? ?//-1 方法
? ?public void decr() throws InterruptedException {
? ? ? ?//上锁
? ? ? ?lock.lock();
? ? ? ?try {
? ? ? ? ? ?//判断:number值是否是1,如果不是1,等待
? ? ? ? ? ?// if (number != 1) { --> 会产生虚假唤醒问题,即只对第一次进入的条件进行了判断,使用while判断可以避免虚假唤醒
? ? ? ? ? ?while (number != 1) {
? ? ? ? ? ? ? ?condition.await();
? ? ? ? ? }
? ? ? ? ? ?//干活:如果number值是1,就-1操作
? ? ? ? ? ?number--;
? ? ? ? ? ?System.out.println("当前线程名:" + Thread.currentThread().getName() + " :: " + number);
? ? ? ? ? ?//通知:通知其他线程
? ? ? ? ? ?condition.signalAll();
? ? ? } finally {
? ? ? ? ? ?//解锁
? ? ? ? ? ?lock.unlock();
? ? ? }
? }
}
?
public class ThreadDemo2 {
? ?public static void main(String[] args) {
? ? ? ?Share share = new Share();
? ? ? ?// 创建线程 AA
? ? ? ?new Thread(() -> {
? ? ? ? ? ?for (int i = 1; i <= 10; i++) {
? ? ? ? ? ? ? ?try {
? ? ? ? ? ? ? ? ? ?// 调用 +1 方法
? ? ? ? ? ? ? ? ? ?share.incr();
? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? }, "AA").start();
? ? ? ?// 创建线程 BB
? ? ? ?new Thread(() -> {
? ? ? ? ? ?for (int i = 1; i <= 10; i++) {
? ? ? ? ? ? ? ?try {
? ? ? ? ? ? ? ? ? ?// 调用 -1 方法
? ? ? ? ? ? ? ? ? ?share.decr();
? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? }, "BB").start();
? ? ? ?// 创建线程 CC
? ? ? ?new Thread(() -> {
? ? ? ? ? ?for (int i = 1; i <= 10; i++) {
? ? ? ? ? ? ? ?try {
? ? ? ? ? ? ? ? ? ?// 调用 +1 方法
? ? ? ? ? ? ? ? ? ?share.incr();
? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? }, "CC").start();
? ? ? ?// 创建线程 DD
? ? ? ?new Thread(() -> {
? ? ? ? ? ?for (int i = 1; i <= 10; i++) {
? ? ? ? ? ? ? ?try {
? ? ? ? ? ? ? ? ? ?// 调用 -1 方法
? ? ? ? ? ? ? ? ? ?share.decr();
? ? ? ? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? ? ? ? ?e.printStackTrace();
? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? }, "DD").start();
? }
?
}
输出:
com.study.lock.ThreadDemo2 当前线程名:AA :: 1 当前线程名:BB :: 0 当前线程名:AA :: 1 当前线程名:BB :: 0 当前线程名:AA :: 1 当前线程名:BB :: 0 当前线程名:AA :: 1 当前线程名:BB :: 0 当前线程名:AA :: 1 当前线程名:BB :: 0 当前线程名:AA :: 1 当前线程名:BB :: 0 当前线程名:AA :: 1 当前线程名:BB :: 0 当前线程名:AA :: 1 当前线程名:BB :: 0 当前线程名:AA :: 1 当前线程名:BB :: 0 当前线程名:AA :: 1 当前线程名:BB :: 0 当前线程名:CC :: 1 当前线程名:DD :: 0 当前线程名:CC :: 1 当前线程名:DD :: 0 当前线程名:CC :: 1 当前线程名:DD :: 0 当前线程名:CC :: 1 当前线程名:DD :: 0 当前线程名:CC :: 1 当前线程名:DD :: 0 当前线程名:CC :: 1 当前线程名:DD :: 0 当前线程名:CC :: 1 当前线程名:DD :: 0 当前线程名:CC :: 1 当前线程名:DD :: 0 当前线程名:CC :: 1 当前线程名:DD :: 0 当前线程名:CC :: 1 当前线程名:DD :: 0
3.2)多线程编程步骤
- 创建资源类,在资源类中创建属性和操作方法
- 在资源类中操作方法【判断 、干活 、通知 】
- 创建多个线程,调用资源类的操作方法
- 防止虚假唤醒问题
|