请说出你所知道的线程同步的方法。_你知道怎么实现多线程同步问题吗?join不算还知道吗...

多线程

线程:类似执行一个命令,多线程:并发执行多条命令。

多线程的优点: 1.充分利用cpu的性能。 2.提高系统性能。 3.同一时刻处理可以处理不同的命令

线程同步

即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,为什么需要它呢?

1.多线程会出现线程安全问题,线程同步可以有效的保证线程安全。 2.当主线程依赖两个子线程结果的时候,需要线程同步

如何实现线程同步?

1.加锁,如:synchronized。

2.通过wait和和notify(和notifyAll),推荐使用notifyAll。

3.线程池callback。

4.join()。

5.CountDownLatch(java SDK包)。

6.CyclicBarrier(java SDK包)。

等等。。。。。。。。。。。。。。。。

这里只介绍5、6两种

我们先来看一个没有加入线程同步的代码:

public static  void hello(){        System.out.println("线程:"+Thread.currentThread().getName()+" 执行了。。。。。。。。。");    }    public static void main(String[] args) {        //线程1        Thread t1 = new Thread(() -> {            hello();        });        t1.start();        //线程2        Thread t2 = new Thread(() -> {            hello();        });        t2.start();        System.out.println("主函数执行完毕。。。。。。。。。");    }

打印结果:

8a1c5e166992fbf6d5f6c1f5b3e3c733.png

main方法的输出语句居然比两个子线程先执行,为什么呢?因为main是主线程,t1、t2是两个子线程,由于线程的执行顺序是无序的,所以就会导致每次的执行结果都不相同,现在我想实现当t1、t2执行完成之后在执行main方法的输出语句,该如何实现呢?只需要给t1、t2分别加一个方法即可:

public static  void hello(){        System.out.println("线程:"+Thread.currentThread().getName()+" 执行了。。。。。。。。。");    }    public static void main(String[] args) {        //线程1        Thread t1 = new Thread(() -> {            hello();        });        t1.start();        //线程2        Thread t2 = new Thread(() -> {            hello();        });        t2.start();        try {            t1.join();            t2.join();        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("主函数执行完毕。。。。。。。。。");    }

结果如下:

176e27d72ad9059766d213df799fdf69.png

为什么join(),可以实现线程同步呢?join()源码如下:

333aa17e221faa56c93f32338ca8c491.png

很明显,这里使用while做了循环等待,让线程不往下执行,达到线程同步(等待)的效果。

然而我们平时的开发过程中基本不会这么创建线程,一般都是使用线程池,那在使用线程池的情况下如何让线程实现同步呢?

我们先试试自己写一个方法让它实现同步,代码如下:

    public static  void hello(){        String name = Thread.currentThread().getName();        try {            System.out.println("线程:"+name+" 休眠开始。。。。。。。。。。。。");            Thread.sleep(1000);            System.out.println("线程:"+name+" 休眠结束。。。。。。。。。。。。");        } catch (InterruptedException e) {            e.printStackTrace();        }    }    public static  void main(String[] args)  {        // 计数器初始化为2        AtomicInteger count = new AtomicInteger(2);        executor.execute(() ->{            hello();            count.decrementAndGet();        });        executor.execute(() ->{            hello();            count.decrementAndGet();        });        //等待两个线程执行完毕        while(count.get() != 0){        }        System.out.println("我是在两个线程执行之后才执行的内容");    }count:用于统计线程执行的数量,线程执行-1;AtomicInteger:原子类,可以在多线程中保证共享变量的安全。decrementAndGet:自减并返回自减以后的结果(原子操作)。while:线程同步的重点:这里主要是让主线程处于循环状态,直到count被减为0,也就意味着两个子线程都已执行完毕。

但是我不推荐这么做,为什么呢?因为java SDK给我们提供了现成的方法,我们为啥还要自己去手动实现呢?下面我们就来看看 CountDownLatch是如何实现线程同步:

// 创建2个线程的线程池    private static  Executor executor =   Executors.newFixedThreadPool(2);    public static  void hello(){        String name = Thread.currentThread().getName();        try {            System.out.println("线程:"+name+" 休眠开始。。。。。。。。。。。。");            Thread.sleep(1000);            System.out.println("线程:"+name+" 休眠结束。。。。。。。。。。。。");        } catch (InterruptedException e) {            e.printStackTrace();        }    }    public static  void main(String[] args)  {                //这里需要注意一点,那就是实例化CountDownLatch的初始大小,一定要和你需要等待线程的数量相同,        //小了会导致等待的线程提前执行。        //大了会导致线程一直处于无限循环当中        CountDownLatch countDownLatch = new CountDownLatch(2);        executor.execute(() ->{            hello();            countDownLatch.countDown();        });        executor.execute(() ->{            hello();            countDownLatch.countDown();        });        //等待两个线程执行完毕        try {            countDownLatch.await();        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("我是在两个线程执行之后才执行的内容");    }

为了效果明显,我特意在hello方法让线程休眠1秒。 countDownLatch:实现线程同步的关键,实例化一个需要等待的线程数量 countDownLatch.countDown():等待线程数-1。 countDownLatch.await();让主线程处于等待状态,直到等待的线程被减为0(注意:这里必须要做异常捕获线程中断的异常:(InterruptedException);

上面代码结果如下:

42137b05123d3bdeee2bb04dc63d400d.png

这里需要注意一点:CountDownLatch的初始大小是不会被重置的,所以使用这个解决方案的时候需要手动重置CountDownLatch线程等待的初始大小。 实现原理:

4ed619b89261c2aa1b5c68f53d390f54.png

其实查看源码,他的实现方式和我之前使用的while类似,他这里用了for的无限循环,直到等待的线程被减为0;

那有没有不需要重新设置线程等待的工具类呢?肯定是有的,那就是接下来要说的:CyclicBarrier

CyclicBarrier

主要通过线程回调来实现线程等待,这里的实现方式稍微做了一下修改:

// 创建3个线程的线程池,其中一个线程用于回调处理主线程的事情    private static  Executor executor =   Executors.newFixedThreadPool(3);    public static  void hello(){        String name = Thread.currentThread().getName();        try {            System.out.println("线程:"+name+" 休眠开始。。。。。。。。。。。。");            Thread.sleep(1000);            System.out.println("线程:"+name+" 休眠结束。。。。。。。。。。。。");        } catch (InterruptedException e) {            e.printStackTrace();        }    }    public static  void main(String[] args)  {        //这里需要注意一点,那就是实例化CountDownLatch的初始大小,一定要和你需要等待线程的数量相同,        //小了会导致等待的线程提前执行。        //大了会导致线程一直处于无限循环当中        final CyclicBarrier barrier = new CyclicBarrier(2, ()->{ executor.execute(()->printAfter()); });        executor.execute(() ->{            hello();            try {                barrier.await();            } catch (Exception e) {                e.printStackTrace();            }        });        executor.execute(() ->{            hello();            try {                barrier.await();            } catch (Exception e) {                e.printStackTrace();            }        });    }    /**     * 两个线程执行完毕之后执行此方法     */    private static void printAfter() {        System.out.println("我是在两个线程执行之后才执行的内容");    }
72774ef00382366a2fa3eee11dba6480.png

这里需要注意一点,那就是主函数的输出语句已经不放在mian方法中了,而是写在了barrier的回调方法中。当等待的线程执行完毕之后CyclicBarrier的等待线程数会被重置。

CyclicBarrier与CountDownLatch区别 CountDownLatch:解决一个线程等待多个线程场景。 CyclicBarrier:解决一组线程之间的等待场景。 CyclicBarrier支持重置功能,CountDownLatch不支持,这点需要特别注意。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值