Java并发编程:两个线程交替打印0-100的奇偶数

问题描述:两个线程交替打印输出数字0~100,一个线程只打印偶数,另一个只打印奇数

方案一:使用synchronized关键字

  • 创建两个线程,一个线程处理偶数,一个线程处理奇数,两个线程之间通过synchronized进行同步,保证count++每次只有一个线程进行操作

  • 为什么两个线程能交替执行,这里很巧的是count从0123...自增过程就是一个奇偶数交替的过程,实际上两个线程都是在不停的尝试(while循环)进入synchronized代码块,如果满足相对应的条件(偶数或是奇数)就打印输出。

package com.hs.demo.code;

/**
 * 两个线程交替打印0~100的奇偶数,用synchroized关键字实现
 *
 * 这种写法有很多多余操作.就是同一个线程会出现多次抢到了锁,但是不满足条件跳过了if语句并不会执行 
 * count++,这种写法效率并不高效
 */
public class SynchroizedPrintOddEven
{
    private static int count;
    //临界资源
    private static  final Object lock = new Object();
    //新建两个线程,第1个只处理偶数,第二个只处理奇数(用位运算),用synchronized来通讯
   
    public static void main(String[] args)
    {

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (count < 100)
                {
                    synchronized (lock){
                        //count & 1 一个数字把它和1做位与的操作,1再二进制就是1,count转换位二进制,和1去与,就是取出count二进制的最低位,最低位是1代表奇数,0代表是偶数,比count%2 == 0 效率高
                        //因为线程是随机抢锁的,可能会出现同一个线程多次进入,但是不满足条件,并不会执行count++.
                        if((count & 1) == 0){
                            System.out.println(Thread.currentThread().getName() + ":" + count++);
                        }
                    }
                }
            }
        },"偶数").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (count < 100){
                    synchronized (lock){
                        //count & 1 一个数字把它和1做位与的操作,1再二进制就是1,count转换位二进制,和1去与,就是取出count二进制的最低位,最低位是1代表奇数,0代表是偶数,比count%2 == 0 效率高
                        if((count & 1) == 1){
                            System.out.println(Thread.currentThread().getName() + ":" + count++);
                        }
                    }
                }
            }
        },"奇数").start();
    }
}

输出结果:

Java并发编程:两个线程交替打印0-100的奇偶数_第1张图片

 结果看起来没问题,奇偶数有交替运行。但实际上并不代表着这两个线程在交替运行,因为线程是随机抢锁的,有可能连续十次都是偶数线程好运抢到了锁,只是因为不满足条件,没有对 count 进行 +1,白白浪费一次占用资源的机会。

因此,这种方式虽然能实现,但存在资源浪费,只需要在两个线程拿到锁时输出语句,即可看到存在大量的资源浪费。


方案二:使用wait/notify关键字(推荐)

  • 这个相对于上面那个好理解很多,直接走流程,偶数线程拿到锁打印输出同时count++,然后进行休眠,因为wait()方法的特性,休眠的同时会释放monitor锁,奇数线程就可以进来了,进来后打印输出,同时notify唤醒偶数线程继续下一轮,奇数线程往下执行wait方法休眠,就这样,偶数线程唤醒奇数线程,奇数线程唤醒偶数线程,直到满足count<100条件后,线程不再休眠,直接退出程序。

  • 这个要点一个在于wait/notify的等待唤醒机制,一个在于wait()方法的特性,休眠后会释放锁。

  • 这种方式和上面那种方式不同点在于,这种方式是被动唤醒的机制,而上面那个是线程不断重试的机制(一直while重试,直到满足条件就打印),很明显这种方式优于上面那种!

public class WaitNotifyPrintOddEven
{
    private static int count = 0;
    //当前线程必须拥有此对象的锁,才能调用某个对象的wait()方法能让当前线程阻塞,
    private static final Object lock = new Object();

    public static void main(String[] args)
    {
        new Thread(new TurningRunner(),"偶数").start();
        new Thread(new TurningRunner(),"奇数").start();
    }

    //拿到锁,我们就打印,一旦打印完唤醒其他线程就休眠
    static  class TurningRunner implements Runnable
    {
        @Override
        public void run()
        {
            while (count <= 100)
            {
                synchronized (lock)
                {
                    System.out.println(Thread.currentThread().getName()+":"+ count++);
                    lock.notify();
                    if(count<=100)
                    {
                        try {
                            //如果任务没结束,唤醒其他线程,自己休眠
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

输出结果: 

Java并发编程:两个线程交替打印0-100的奇偶数_第2张图片

这种方式,每次抢到资源都是有意义的,效率更高


Java并发编程-两个线程交替打印0-100的奇偶数

聊聊并发编程的10个坑

你可能感兴趣的:(Java基础,wait-notify交替打印,synchroized交替打印)