java中使用三个线程依次打印1,2,3

面试这道题没写出来,无了,真想一头撞死,太菜了。。。不太适合这个行业

目录

第一种:使用Lock

第二种:JMM解决

 第三种:使用AtomicInteger,自带并发安全

总结


有三个线程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却忘了,真是。。。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值