多线程交替输出
发布于: 2021 年 07 月 28 日
1. 概括
多线程输出循环输出,是并发编程面试常问的问题之一,解决这类问题主要可以分成两种方案
ReentrantLock 方案
注意点:一个线程一个 condition ,不要乱
注意点:
condition
的方法是await()和 singal()
不是wait()
syn 方案
多线程的时候
notifyAll()
和 和notify()
要注意区分需要有一个 who 的变量,标定当前是轮到谁执行了
必须先进行阻塞, 然后输出, 最后唤醒,顺序不能变!!!
如果是先输出,在阻塞,最后唤醒会导致第一次打印时候可能错误(具体看下面)
在
reentrantLock
下, 阻塞判断使用if
或者while
都可以在 syn 下 3 线程的阻塞判断 阻塞只能使用 while(双线程可以用
if
)这是由于reentrantLock
可以让 唤醒指定的线程,而syn
在三线程情况下只能notifyAll
,进行唤醒,这时候需要将其中一个线程进行再次阻塞
2. 两个线程循环打印 AB
syn 法
public class AB_syn {
static int count = 0;
public static void main(String[] args) {
Object lock = new Object();
new Thread(() -> {
// 先进入到锁里面,才能进行控制
synchronized (lock) {
// 死循环,进行循环打印
while (true) {
// 如果count 不等于 0, 就说明是另一个线程需要打印,自身需要wait();
while (count != 0) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("A");
// 唤醒线程2
count = 1;
// 这时候唤醒了其他在的线程,会继续执行到 while(count!=0)里面去阻塞
lock.notify();
}
}
}).start();
new Thread(() -> {
// 先都丢到锁里面,才能进行控制
synchronized (lock) {
// 死循环,进行循环打印
while (true) {
while (count != 1) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("B");
count = 0;
lock.notify();
}
}
}).start();
}
}
复制代码
复制代码
reentrantlock
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class AB_reentrylock {
static int who = 0;
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
new Thread(() -> {
try {
// 先进入锁里面,才能进行控制
lock.lock();
while (true) {
while (who != 0) {
conditionA.await();
}
System.out.println("A");
// 先唤醒B, 让B 到 AQS中排队
conditionB.signal();
who = 1;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}).start();
new Thread(() -> {
try {
// 先进入锁里面,才能进行控制
lock.lock();
while (true) {
while (who != 1) {
conditionB.await();
}
System.out.println("B");
// 先唤醒A, 让A 到 AQS中排队
conditionA.signal();
who = 0;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}).start();
}
}
复制代码
复制代码
3. 三线程打印 ABC
syn 法
public class ABC_syn {
static int who = 0;
public static void main(String[] args) {
Object lock = new Object();
new Thread(() -> {
synchronized (lock) {
while (true) {
while (who != 0) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("A");
who = 1;
lock.notifyAll();
}
}
}).start();
new Thread(() -> {
synchronized (lock) {
while (true) {
while (who != 1) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("B");
who = 2;
lock.notifyAll();
}
}
}).start();
new Thread(() -> {
synchronized (lock) {
while (true) {
while (who != 2) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("C");
who = 0;
lock.notifyAll();
}
}
}).start();
}
}
复制代码
复制代码
reentrantlock
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ABC_reentrylock {
static int count = 0;
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
lock.notifyAll();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
new Thread(() -> {
try {
// 加锁,进入并发控制内部
lock.lock();
while (true) {
while (count != 0) {
conditionA.await();
}
System.out.println("A");
// 将阻塞在 conditionB 的线程唤醒
conditionB.signal();
count = 1;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}).start();
new Thread(() -> {
try {
// 加锁,进入并发控制内部
lock.lock();
while (true) {
while (count != 1) {
conditionB.await();
}
System.out.println("B");
// 将阻塞在 conditionB 的线程唤醒
conditionC.signal();
count = 2;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}).start();
new Thread(() -> {
try {
// 加锁,进入并发控制内部
lock.lock();
while (true) {
while (count != 2) {
conditionC.await();
}
System.out.println("C");
// 将阻塞在 conditionB 的线程唤醒
conditionA.signal();
count = 0;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}).start();
}
}
复制代码
复制代码
4. 为什么需要先进行判断阻塞,在打印?
这种情况下,如果线程 2 先开启执行,就会导致第一次输出的时候 B 先进行输出,三个线程的时候,同样无法保证第一个输出的是 A
public class AB_syn2 {
static int count = 0;
public static void main(String[] args) {
Object lock = new Object();
new Thread(() -> {
// 先都丢到锁里面,才能进行控制
synchronized (lock) {
// 死循环,进行循环打印
while (true) {
System.out.println("A");
// 唤醒线程2
count = 1;
lock.notify();
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(() -> {
// 先都丢到锁里面,才能进行控制
synchronized (lock) {
// 死循环,进行循环打印
while (true) {
System.out.println("B");
count = 0;
lock.notify();
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
复制代码
复制代码
5. 写一个死锁
死锁的四个要素:
互斥访问
请求和保持
不剥夺
循环等待
简单来说就是,两个锁(AB),一个线程拿了 A,一个线程拿了 B, 互相请求对方的资源
public class 死锁 {
public static void main(String[] args) {
Object A = new Object();
Object B = new Object();
new Thread(() -> {
synchronized (A) {
System.out.println("获得了A");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("100毫秒后尝试获取B");
synchronized (B){
System.out.println("bbbb");
}
}
}).start();
new Thread(() -> {
synchronized (B) {
System.out.println("获得了B");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("100毫秒后尝试获取A");
synchronized (A){
System.out.println("aaaa");
}
}
}).start();
}
}
复制代码
划线
评论
复制
发布于: 2021 年 07 月 28 日阅读数: 10
愚者
关注
还未添加个人签名 2021.07.22 加入
还未添加个人简介
评论