多线程交叉打印数字,线程切换,结果通知

前言

一道面试题,两个线程交叉打印奇偶数。核心是多线程交替切换。

1. synchronized锁机制,wait和notifyAll方法

本质锁竞争

仅在线程需要的时候持有锁,其余时间检查自身线程锁,释放线程自己持有的锁。

public class Test2 {

    public static void main(String[] args) {
        Num num = new Num();

        //线程1-奇数线程
        new Thread(() -> {
            while (num.number < 100){
                synchronized (num){
                    //奇数
                    if (num.number%2 == 1){
                        System.out.println(Thread.currentThread().getName() + "\t奇数线程:" + num.number);
                        num.number++;
                        num.notifyAll();
                    } else {
                        try {
                            num.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }).start();

        //线程2-偶数线程
        new Thread(() -> {
            while (num.number < 100){
                synchronized (num){
                    //奇数
                    if (num.number%2 == 1){
                        try {
                            num.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } else {
                        System.out.println(Thread.currentThread().getName() + "\t偶数线程:" + num.number);
                        num.number++;
                        num.notifyAll();
                    }
                }
            }
        }).start();
    }


    static class Num {
        public int number = 1;
    }
}

代码很简单,通过wait和notifyAll方法来释放自身线程锁和让其他线程持有锁。

这里要注意:使用synchronized加锁必须是对象,synchronized就是通过对象头的方式加锁的。

2. ReentrantLock实现

public class Test21 {

    public static void main(String[] args) {
        Num num = new Num();
        ReentrantLock lock = new ReentrantLock();
        //奇数条件
        Condition oddCondition = lock.newCondition();
        //偶数条件
        Condition evenCondition = lock.newCondition();

        //线程1-奇数线程
        new Thread(() -> {
            while (num.number < 100) {
                lock.lock();

                try {
                    //奇数
                    if (num.number % 2 == 1) {
                        System.out.println(Thread.currentThread().getName() + "\t奇数线程:" + num.number);
                        num.number++;
                        evenCondition.signal();
                    } else {
                        oddCondition.await();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }).start();

        //线程2-偶数线程
        new Thread(() -> {
            while (num.number < 100) {
                lock.lock();

                try {
                    //奇数
                    if (num.number % 2 == 1) {
                        evenCondition.await();
                    } else {
                        System.out.println(Thread.currentThread().getName() + "\t偶数线程:" + num.number);
                        num.number++;
                        oddCondition.signal();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
        }
    }).start();
}


static class Num {
    public int number = 1;
}
}

 ReentrantLock的condition控制更加精准,可以细节到具体的线程

ReentrantLock可以对每一个线程设置一个Condition,控制到具体的单个线程,是notify所不具备的。

3. 总结

这里的思想在RPC框架中非常常用。

1. 发起RPC的调用的时候,程序不会立马返回,需要将自身线程等待

2. 调用结果返回时,需要唤醒自身线程。

3. 通知机制,这里使用notifyAll通知,而RPC一般使用观察者模式,回调通知。

猜你喜欢

转载自blog.csdn.net/fenglllle/article/details/83018521