多线程并发,主要是多线程操作资源类,其次是多线程间的通信,三部曲,判断、干活、通知,为了防止虚假唤醒判断时必须使用
while
,最后,为了保证多线程执行的顺序性,可以使用Condition
配合标志位来通知唤醒指定线程。
生产者消费者
两个线程操作空调资源类,一个升温一度,一个减温一度,一来一回,循环10
轮
版本一
多线程间通信三部曲,判断、干活、通知唤醒其它线程
package com.zbiti.juc;
//高内聚低耦合,多线程操作资源类
//多线程通信 判断/干活/通知 为了防止虚假唤醒判断用while
public class ProducerConsumerDemo {
public static void main(String[] args) {
AirConditon airConditon = new AirConditon();
new Thread(()->{
try {
for(int i=1;i<=10;i++){
airConditon.increament();
}
} catch (Exception e) {
e.printStackTrace();
}
},"AA").start();
new Thread(()->{
try {
for(int i=1;i<=10;i++){
airConditon.decreament();
}
} catch (Exception e) {
e.printStackTrace();
}
},"BB").start();
}
}
//空调资源类 带加一度、减一度功能
class AirConditon {
int number = 0;
synchronized void increament() throws Exception {
//判断
if (number != 0) {
this.wait();
}
//干活
number++;
System.out.println(Thread.currentThread().getName()+"\t"+number);
//通知
this.notifyAll();
}
synchronized void decreament() throws Exception {
//判断
if (number != 1) {
this.wait();
}
//干活
number--;
System.out.println(Thread.currentThread().getName()+"\t"+number);
//通知
this.notifyAll();
}
}
结果
此时结果是正确的
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
Process finished with exit code 0
如果是换成是4
个线程呢,两个生产者,两个消费者
package com.zbiti.juc;
//高内聚低耦合,多线程操作资源类
//多线程通信 判断/干活/通知 为了防止虚假唤醒判断用while
public class ProducerConsumerDemo {
public static void main(String[] args) {
AirConditon airConditon = new AirConditon();
new Thread(()->{
try {
for(int i=1;i<=10;i++){
airConditon.increament();
}
} catch (Exception e) {
e.printStackTrace();
}
},"AA").start();
new Thread(()->{
try {
for(int i=1;i<=10;i++){
airConditon.decreament();
}
} catch (Exception e) {
e.printStackTrace();
}
},"BB").start();
new Thread(()->{
try {
for(int i=1;i<=10;i++){
airConditon.increament();
}
} catch (Exception e) {
e.printStackTrace();
}
},"CC").start();
new Thread(()->{
try {
for(int i=1;i<=10;i++){
airConditon.decreament();
}
} catch (Exception e) {
e.printStackTrace();
}
},"DD").start();
}
}
//空调资源类 带加一度、减一度功能
class AirConditon {
int number = 0;
synchronized void increament() throws Exception {
//判断
if (number != 0) {
this.wait();
}
//干活
number++;
System.out.println(Thread.currentThread().getName()+"\t"+number);
//通知
this.notifyAll();
}
synchronized void decreament() throws Exception {
//判断
if (number != 1) {
this.wait();
}
//干活
number--;
System.out.println(Thread.currentThread().getName()+"\t"+number);
//通知
this.notifyAll();
}
}
结果
可以看到结果并不符合我们预期的
AA 1
DD 0
CC 1
AA 2
CC 3
DD 2
CC 3
BB 2
AA 3
BB 2
CC 3
DD 2
CC 3
BB 2
AA 3
BB 2
CC 3
DD 2
CC 3
BB 2
AA 3
BB 2
CC 3
DD 2
CC 3
BB 2
AA 3
BB 2
CC 3
DD 2
BB 1
BB 0
AA 1
DD 0
AA 1
DD 0
AA 1
DD 0
AA 1
DD 0
Process finished with exit code 0
版本二
为了防止虚假唤醒,使用while
进行判断
版本三
使用ReentrantLock
,区别是等待和通知唤醒的使用的是Condition
的condition.await();
和condition.signalAll();
package com.zbiti.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//高内聚低耦合,多线程操作资源类
//多线程通信 判断/干活/通知 为了防止虚假唤醒判断用while
public class ProducerConsumerDemo {
public static void main(String[] args) {
AirConditon airConditon = new AirConditon();
new Thread(() -> {
try {
for (int i = 1; i <= 10; i++) {
airConditon.increament();
}
} catch (Exception e) {
e.printStackTrace();
}
}, "AA").start();
new Thread(() -> {
try {
for (int i = 1; i <= 10; i++) {
airConditon.decreament();
}
} catch (Exception e) {
e.printStackTrace();
}
}, "BB").start();
new Thread(() -> {
try {
for (int i = 1; i <= 10; i++) {
airConditon.increament();
}
} catch (Exception e) {
e.printStackTrace();
}
}, "CC").start();
new Thread(() -> {
try {
for (int i = 1; i <= 10; i++) {
airConditon.decreament();
}
} catch (Exception e) {
e.printStackTrace();
}
}, "DD").start();
}
}
//空调资源类 带加一度、减一度功能
class AirConditon {
int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
void increament() throws Exception {
lock.lock();
try {
//判断
while (number != 0) {
condition.await();
}
//干活
number++;
System.out.println(Thread.currentThread().getName() + "\t" + number);
//通知
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
void decreament() throws Exception {
lock.lock();
try {
//判断
while (number != 1) {
condition.await();
}
//干活
number--;
System.out.println(Thread.currentThread().getName() + "\t" + number);
//通知
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
结果
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
Process finished with exit code 0
Condition
通知唤醒特定线程
多线程通信的原则,判断、干活、通知
使用Condition
配合标志位通知特定线程干活,通知前需要修改标志位
package com.zbiti.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//三个线程 A B C ,A打印5次,B打印10次,C打印15次,循环10轮
public class ConditionDemo {
public static void main(String[] args) {
ShareData shareData = new ShareData();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
shareData.print5();
}
}, "A").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
shareData.print10();
}
}, "B").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
shareData.print15();
}
}, "C").start();
}
}
//资源类
class ShareData {
Lock lock = new ReentrantLock();
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
Condition c3 = lock.newCondition();
//标志位1线程A执行,2线程B执行,3线程C执行
int number = 1;
void print5() {
lock.lock();
try {
//判断
while (number != 1) {
c1.await();
}
//干活
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
//通知前改变标志位
number = 2;
c2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
void print10() {
lock.lock();
try {
//判断
while (number != 2) {
c2.await();
}
//干活
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
//通知
number = 3;
c3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
void print15() {
lock.lock();
try {
//判断
while (number != 3) {
c3.await();
}
//干活
for (int i = 1; i <= 15; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
//通知
number = 1;
c1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
小总结
线程间通信三部曲,判断、干活、通知唤醒,为了防止虚假唤醒,使用while
进行判断
synchronized
版本
ReentrantLock
版本
Condition
通知唤醒特定线程,通知唤醒前修改标志位
本文由博客一文多发平台 OpenWrite 发布!