两个线程分别打印0-100的之间的奇偶数(两种方式对比)

业务描述

前段时间遇到一个题目,关于两个线程分别打印0-100的之间的奇偶数,当时自己的写法是通过synchronized同步锁结合wait 和notify 的方式进行编写。主要逻辑的代码如下所示:

package com.xyq.maventest.alibaba;

import java.util.concurrent.CountDownLatch;

/****
 * 两个线程分别打印0-100之间的奇偶数
 * @author youqiang.xiong
 * <p> TODO 简单描述此类的用途</p>
 * 2018年2月25日下午6:18:34
 */
public class ThreadPrintData {

    public static CountDownLatch latch = new CountDownLatch(2);

    private static Object lock = new Object();

    private static volatile Integer i = 0;
    private static final int TOTAL = 100;


    public static void main(String[] args) {

        Thread thread1 = new Thread() {

            @Override
            public void run() {
                while (i <= TOTAL) {
                    synchronized (lock) {
                        if (i % 2 == 1) {
                            System.out.println(Thread.currentThread().getName() + "打印:   " + i++);
                        } else {
                            lock.notifyAll();
                            try {
                                if(i <= TOTAL){
                                    lock.wait();
                                }
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }

                }
            }
        };

        thread1.setName("奇数线程");
        Thread thread2 = new Thread() {

            @Override
            public void run() {
                while (i <= TOTAL) {
                    synchronized (lock) {
                        if (i % 2 == 0) {
                            System.out.println(Thread.currentThread().getName()+ "打印:  " + i++);
                        } else {
                            lock.notifyAll();
                            try {
                                if(i < TOTAL){
                                    lock.wait();
                                }
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }

                }
            }
        };
        thread2.setName("偶数线程");

        thread1.start();
        thread2.start();
    }
}

控制台打印结果如下:

这里写图片描述

此段代码的结果虽能正确打印出结果,但是请注意Console中的红色标注处,此时的控制台并未结束,这是为什么呢?

可以打开控制jvisualvm 监控界面,打开应用程序,可以发现到此时有一个用户现场一直处于waiting 状态

这里写图片描述

为什么会等待也是因为偶数线程打印完后,并未唤醒奇数线程,导致奇数线程一直等待。

其实还有另外一种实现方式,不需要进行任何加锁,利用并发包中的AtomicInteger和volidate修饰符组合,详细代码如下所示:

package com.xyq.maventest.alibaba;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

/****
 * 两个线程分别打印奇偶数(优化后的策略)
* @ClassName: AlibabaTest
* @Description: TODO(这里用一句话描述这个类的作用)
* @author youqiang.xiong
* @date 2018年3月11日
*
 */
public class AlibabaTest {

    private static volatile Boolean flag = true;

    private static AtomicInteger num = new AtomicInteger();

    private static final Integer TOTAL = 100;

    public static CountDownLatch latch = new CountDownLatch(2);

    public static void main(String[] args) throws InterruptedException {

        long start =  System.currentTimeMillis();


        Thread jsThread = new Thread(new Runnable() {

            @Override
            public void run() {

                while(num.get() <= TOTAL-1){
                    if(!flag){
                        System.out.println(Thread.currentThread().getName()+"打印: " + num.getAndIncrement());
                        flag = true;
                    }
                }

                latch.countDown();

            }
        });

        jsThread.setName("奇数线程");

        Thread osThread = new Thread(new Runnable() {

            @Override
            public void run() {

                while(num.get() <= TOTAL){
                    if(flag){
                        System.out.println(Thread.currentThread().getName()+ "打印: " + num.getAndIncrement());
                        flag = false;
                    }
                }

                latch.countDown();
            }
        });

        osThread.setName("偶数线程");

        osThread.start();
        jsThread.start();

        latch.await();
        long end =  System.currentTimeMillis();

        System.out.println("一共耗时:"+(end - start) + "ms");

    }

}

运行main方法,结果如下所示:

这里写图片描述

注意console的的结果,首先是可以正常满足业务需求,其次程序运行结束后,也意味着整个程序结束,并未出现线程等待的情况,原因是因为这个程序并未有任何加锁的地方,从这一点上其实比第一种方法的体验会更好。

性能对比

第一种方法通过加锁方式控制,假如需要打印的0-100万,或者更大,线程之间的切换肯定会导致性能比较差,为了更加直观地比较两种实现方法的性能差别,可以利用CountDownLatch 的类,可以控制两个线程执行结束后,主线程再继续执行,由于方法二代码已加CountDownLatch 统计时间,此处对方法一的代码进行改进下,代码如下所示:

package com.xyq.maventest.alibaba;

import java.util.concurrent.CountDownLatch;

/****
 * 两个线程分别打印0-100之间的奇偶数
 * @author youqiang.xiong
 * <p> TODO 简单描述此类的用途</p>
 * 2018年2月25日下午6:18:34
 */
public class ThreadPrintData {

    public static CountDownLatch latch = new CountDownLatch(2);

    private static Object lock = new Object();

    private static volatile Integer i = 0;
    private static final int TOTAL = 1000000;




    public static void main(String[] args) throws InterruptedException {

        long start = System.currentTimeMillis();
        Thread thread1 = new Thread() {

            @Override
            public void run() {
                while (i <= TOTAL) {
                    synchronized (lock) {
                        if (i % 2 == 1) {
                            if(i<=TOTAL){
                                System.out.println(Thread.currentThread().getName() + "打印:   " + i++);
                            }
                        } else {
                            lock.notifyAll();
                            try {
                                if(i < TOTAL){
                                    lock.wait();
                                }
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }

                }

                latch.countDown();
            }
        };

        thread1.setName("奇数线程");
        Thread thread2 = new Thread() {

            @Override
            public void run() {
                while (i <= TOTAL) {
                    synchronized (lock) {
                        if (i % 2 == 0) {
                            System.out.println(Thread.currentThread().getName()+ "打印:  " + i++);
                        } else {
                            lock.notifyAll();
                            try {
                                if(i < TOTAL){
                                    lock.wait();
                                }
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }

                }
                latch.countDown();
            }
        };
        thread2.setName("偶数线程");

        thread1.start();
        thread2.start();

        latch.await();
        long end = System.currentTimeMillis();
        System.out.println("共耗时:"+(end-start) + "ms");
    }
}

测试时,不断掉大TOTAL的值,观察两个方法分别耗时多少。

  • 11
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值