前言:这是一道百度的面试题,查阅资料学习后发现方法还挺多的,这里给出三种方法,分别是:
- 两个锁
- ReenTrantLock加condition来管理
- 一个锁+一个状态位
两个锁
思想:三个线程,每一个线程都有一把锁,当一个线程同时拿到两把锁才能去打印字符,代码如下:
package ConcurrentTest;
public class XuTwoLockPrinter implements Runnable{
// 前一个锁
Object frontLock;
// 自己的锁
Object ownLock;
char printChar;
XuTwoLockPrinter (Object frontLock,Object ownLock,char printChar) {
this.frontLock = frontLock;
this.ownLock = ownLock;
this.printChar = printChar;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (frontLock) {
synchronized (ownLock) {
System.out.print(printChar);
// 唤醒线程
ownLock.notify();
}
if (i < 9) {
try {
//释放锁,等待被唤醒
frontLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Object lockA = new Object();
Object lockB = new Object();
Object lockC = new Object();
Thread printerA = new Thread(new XuTwoLockPrinter(lockC,lockA,'A'));
Thread printerB = new Thread(new XuTwoLockPrinter(lockA,lockB,'B'));
Thread printerC = new Thread(new XuTwoLockPrinter(lockB,lockC,'C'));
printerA.start();
Thread.sleep(100);
printerB.start();
printerC.start();
}
}
注:这里最后一次要做一次判断,不然会引起死锁。
ReenTrantLock加condition来管理
思想:与上面的方法想法类似,代码如下:
package ConcurrentTest;
import org.junit.Test;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class RcSyncPrinter {
@Test
public void test() throws InterruptedException {
ThreadGroup group = new ThreadGroup("xx");
// 写锁
ReentrantLock lock = new ReentrantLock();
// 打印a线程的condition
Condition conditionA = lock.newCondition();
// 打印b线程的condition
Condition conditionB = lock.newCondition();
// 打印c线程的condition
Condition conditionC = lock.newCondition();
// 实例化A线程
Thread printerA = new Thread(group, new Printer(lock, conditionA, conditionB, 'A'));
// 实例化B线程
Thread printerB = new Thread(group, new Printer(lock, conditionB, conditionC, 'B'));
// 实例化C线程
Thread printerC = new Thread(group, new Printer(lock, conditionC, conditionA, 'C'));
// 依次开始A B C线程
printerA.start();
Thread.sleep(100);
printerB.start();
Thread.sleep(100);
printerC.start();
// 主线程循环让出CPU使用权
while (group.activeCount() > 0) {
Thread.yield();
}
}
// 打印线程
private class Printer implements Runnable {
// 打印次数
private static final int PRINT_COUNT = 6;
// 打印锁
private final ReentrantLock reentrantLock;
// 本线程打印所需的condition
private final Condition thisCondtion;
// 下一个线程打印所需要的condition
private final Condition nextCondtion;
// 打印字符
private final char printChar;
public Printer(ReentrantLock reentrantLock, Condition thisCondtion, Condition nextCondition, char printChar) {
this.reentrantLock = reentrantLock;
this.nextCondtion = nextCondition;
this.thisCondtion = thisCondtion;
this.printChar = printChar;
}
@Override
public void run() {
// 获取打印锁 进入临界区
reentrantLock.lock();
try {
// 连续打印PRINT_COUNT次
for (int i = 0; i < PRINT_COUNT; i++) {
System.out.print(printChar);
// 使用nextCondition唤醒下一个线程
// 因为只有一个线程在等待,所以signal或者signalAll都可以
nextCondtion.signal();
// 不是最后一次则通过thisCondtion等待被唤醒
// 必须要加判断,不然能够打印6次 但6次后就会直接死锁
if (i < PRINT_COUNT - 1) {
try {
// 本线程让出锁并等待唤醒
thisCondtion.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} finally {
// 释放打印锁
reentrantLock.unlock();
}
}
}
}
一个锁+一个状态位
思想:通过状态位来决定到哪个线程来打印。代码为:
package ConcurrentTest;
import org.junit.Test;
public class StateLockPrinter {
//状态变量
private volatile int state=0;
@Test
public void test() throws InterruptedException {
//锁
Object lock=new Object();
ThreadGroup group=new ThreadGroup("xx");
//打印A的线程
Thread threadA=new Thread(group,new Printer(lock, 0,1, 'A'));
//打印B的线程
Thread threadB=new Thread(group,new Printer(lock, 1,2, 'B'));
//打印C的线程
Thread threadC=new Thread(group,new Printer(lock, 2,0, 'C'));
//一次启动A B C线程
threadA.start();
Thread.sleep(1000);
threadB.start();
Thread.sleep(1000);
threadC.start();
//循环检查线程组合的活的线程数量
while (group.activeCount()>0) {
//让出CPU使用权
Thread.yield();
}
}
//打印线程
private class Printer implements Runnable{
//打印次数
private static final int PRINT_COUNT=6;
//打印锁
private final Object printLock;
//打印标志位 和state变量相关
private final int printFlag;
//后继线程的线程的打印标志位,state变量相关
private final int nextPrintFlag;
//该线程的打印字符
private final char printChar;
public Printer(Object printLock, int printFlag,int nextPrintFlag, char printChar) {
super();
this.printLock = printLock;
this.printFlag=printFlag;
this.nextPrintFlag=nextPrintFlag;
this.printChar = printChar;
}
@Override
public void run() {
//获取打印锁 进入临界区
synchronized (printLock) {
//连续打印PRINT_COUNT次
for(int i=0;i<PRINT_COUNT;i++){
//循环检验标志位 每次都阻塞然后等待唤醒
while (state!=printFlag) {
try {
printLock.wait();
} catch (InterruptedException e) {
return;
}
}
//打印字符
System.out.print(printChar);
//设置状态变量为下一个线程的标志位
state=nextPrintFlag;
//注意要notifyall,不然会死锁,因为notify只通知一个,
//但是同时等待的是两个,如果唤醒的不是正确那个就会没人唤醒,死锁了
printLock.notifyAll();
}
}
}
}
}
注:printLock.notifyAll()是随机唤醒,如果唤醒的不是下一个应该打印的线程,也没事,它会继续等待并释放锁。
参考的博文链接为:https://www.cnblogs.com/xiaoxi/p/8035725.html