面试这道题没写出来,无了,真想一头撞死,太菜了。。。不太适合这个行业
目录
有三个线程A,B,C, 三个线程轮流打印出1,2,3;其中线程A只能打印1, 线程B只能打印2, 线程C只能打印3
第一种:使用Lock
思路:使用java中的Lock, 可以通过Lock获取Condition(监听器),这个Condition可以监听注册在上面的线程对象,通过await()和notifyAll()操作线程对象的状态,可以完成“选择性通知”
public class PrintThread {
private Lock lock = new ReentrantLock();
private int flag = 1; // 控制执行哪个线程
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void printA() {
lock.lock();
try {
while (flag != 1) {
condition1.await();
}
System.out.println(Thread.currentThread().getName() + " : 1");
flag = 2;
condition1.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
while (flag != 2) {
condition2.await();
}
System.out.println(Thread.currentThread().getName() + " : 2");
flag = 3;
condition2.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
while (flag != 3) {
condition3.await();
}
System.out.println(Thread.currentThread().getName() + " : 3");
flag = 1;
condition3.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
class Main {
public static void main(String[] args) {
PrintThread printThread = new PrintThread();
for (int i = 0; i < 10; i++) {
new Thread(() -> printThread.printA(), "A").start();
new Thread(() -> printThread.printB(), "B").start();
new Thread(() -> printThread.printC(), "C").start();
}
}
}
第二种:JMM解决
由于线程的循环打印,很大程度遇到的是并发问题,而对于JMM的应对措施主要针对 原子性,可见性,指令重排;
想到上面这三点,我第一想法是处理懒汉式单例模式的双重检测,所以写了下面这种方式:
public class PrintThreadByFlag {
private volatile int flag = 1;
public void printA() {
while (true) {
if (flag == 1) {
synchronized (this) {
if (flag == 1) {
System.out.println(Thread.currentThread().getName() + ": 1");
flag = 2;
}
}
}
}
}
public void printB() {
while (true) {
if (flag == 2) {
synchronized (this) {
if (flag == 2) {
System.out.println(Thread.currentThread().getName() + ": 2");
flag = 3;
}
}
}
}
}
public void printC() {
while (true) {
if (flag == 3) {
synchronized (this) {
if (flag == 3) {
System.out.println(Thread.currentThread().getName() + ": 3");
System.out.println("-----------------------------------");
flag = 1;
}
}
}
}
}
}
class MainByFlag {
public static void main(String[] args) {
PrintThreadByFlag printThreadByFlag = new PrintThreadByFlag();
new Thread(() -> printThreadByFlag.printA(), "A").start();
new Thread(() -> printThreadByFlag.printB(), "B").start();
new Thread(() -> printThreadByFlag.printC(), "C").start();
}
}
从结果来看似乎没有问题:
第三种:使用AtomicInteger,自带并发安全
使用atomic中的AtomicInteger,本身是线程安全的,并且AtomicInteger中的value也是volatile可见的
/**
* 使用AtomicInteger,本身保证原子性
*/
public class AtomicThread extends Thread{
private AtomicInteger currentCount;
private String name;
private static final Integer maxCount = 100;
private static String[] chars = {"1", "2", "3"};
public AtomicThread(String name, AtomicInteger currentCount) {
this.name = name;
this.currentCount = currentCount;
}
@Override
public void run() {
while (currentCount.get() < maxCount) {
if (this.name.equals(chars[currentCount.get() % 3])) {
System.out.println(this.name);
currentCount.getAndIncrement();
if (this.name.equals("3")) {
System.out.println("-------------------");
}
}
}
}
}
class MainByAtomicThread {
public static void main(String[] args) {
AtomicInteger target = new AtomicInteger(0);
new AtomicThread("1", target).start();
new AtomicThread("2", target).start();
new AtomicThread("3", target).start();
}
}
总体来看,123的输出顺序是没有问题,但是这个分割线有点毛病。。并且输出的总量有时候大于maxCount。。。
总结
感觉这道题主要从JMM方面考虑,保证原子性,可见性,指令重排;
想想第二种方法,之前也学过单例模式的线程安全处理,但自己就是想不到;想到了ReetLock但是API却忘了,真是。。。