Java多线程-JUC-1(八)

java,多线程,juc · 浏览次数 : 25

小编点评

**JUC同步器** * **功能**:用于线程之间同步执行代码。 * **主要类型**: * CountDownLatch:用于线程之间计数等待。 * Semaphore:用于线程之间资源共享。 * CyclicBarrier:用于线程间阻塞等待。 * Exchanger:用于线程间数据交换。 **JUC同步器的具体用法** * **CountDownLatch**: * 创建一个计数器,初始值为10。 * 在线程执行代码中,使用`countDown()`方法向计数器递减1。 * 当计数器为0时,线程才继续执行代码。 * **Semaphore**: * 创建一个信号量,初始值为3。 * 在线程执行代码中,使用`acquire()`方法尝试获取信号量。 * 如果获取成功,线程才继续执行代码。 * 在线程释放信号量时,使用`release()`方法释放信号量。 **示例** ```java // CountDownLatch 示例 public class CountDownLatchTester implements Runnable { static final CountDownLatch latch = new CountDownLatch(10); @Override public void run() { try { System.out.println(Thread.currentThread().getName() + \" 检查完毕!\"); } catch (Exception e) { e.printStackTrace(); } finally { latch.countDown(); } } public static void main(String[] args) throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(5); for (int i = 10; i > 0; i--) { Thread.sleep(1000); executor.submit(new CountDownLatchTester()); System.out.println(i); } Thread.sleep(1000); latch.await(); System.out.println(); System.out.println(\"点火,发射!\"); // 关闭线程池 executor.shutdown(); } } // Semaphore 示例 public class SemaphoreTester implements Runnable { static final Semaphore semaphore = new Semaphore(3); @Override public void run() { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName() + \" 开始进餐\"); Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } finally { semaphore.release(); } } } // CyclicBarrier 示例 public class CyclicBarrierTester implements Runnable { Exchanger<Object> exchanger; Object object; public CyclicBarrierTester(Exchanger<Object> exchanger, Object object) { this.exchanger = exchanger; this.object = object; } @Override public void run() { try { Object previous = this.object; this.object = this.exchanger.exchange(this.object); System.out.println(Thread.currentThread().getName() + \" 用对象 \" + previous + \" 换对象 \" + this.object); } catch (InterruptedException e) { e.printStackTrace(); } } } // Exchanger 示例 public class ExchangerTester implements Runnable { Exchanger<Object> exchanger; Object object; public ExchangerTester(Exchanger<Object> exchanger, Object object) { this.exchanger = exchanger; this.object = object; } @Override public void run() { try { Object previous = this.object; this.object = this.exchanger.exchange(this.object); System.out.println(Thread.currentThread().getName() + \" 用对象 \" + previous + \" 换对象 \" + this.object); } catch (InterruptedException e) { e.printStackTrace(); } } } ```

正文

前面把线程相关的生命周期、关键字、线程池(ThreadPool)、ThreadLocal、CAS、锁和AQS都讲完了,现在就剩下怎么来用多线程了。而要想用好多线程,其实是可以取一些巧的,比如JUC(好多面试官喜欢问的JUC,就是现在要讲的JUC)。JUC就是java.util.concurrent的首字母缩写,它是Java并发工具包就是中提供的各种工具类的统称,主要分为几大类:

1、同步器;

2、线程安全的容器;

3、阻塞队列;

4、一些特殊的类。

他们都有各自适合应用场景。这里是并发工具包相关类的继承结构:

 

 

 

下面从同步器开始。

常用的JUC同步器有四个:

1、CountDownLatch:字面意思是倒计时锁,如果有“倒计时”的需求,那么CountDownLatch是最好的工具。它还有一个别称:发令枪。可以想象一下,火箭点火发射的时候,所有设备、部门都会依次检查确认,如果全部都确认准备好了才能开始发射,也就是等倒数到指定的数字(一般是0)的时候,就开始执行预设动作;

2、Semaphore:字面意思信号量,好比红绿灯,或者就餐排队时餐馆发的数字序号,一次只允许若干个线程执行。这个在昨天的例子里面也已经演示过了,而且还是通过自定义AQS来实现的(信号量可能不太好理解,我更倾向于叫它摇号器);

3、CyclicBarrier:字面意思是屏障或者栅栏,与CountDownLatch比较像,但它侧重于工作本身,即指定的若干个工作都满足考核标准(某个屏障)之后,才能继续进行下面的工作,且可反复使用;

4、Exchanger:用于线程之间交换数据,更形象地说法是“交换机”,即当一个线程完成某项工作后想与另一个线程交换数据,就可以使用这个工具类。

下面来一个个地演示它们的用法。

 

一、CountDownLatch

CountDownLatch的功能如果用图来表示的话,就会是这样的:

 

 

 CountDownLatch实例代码:

/**
 * 发令枪
 */
public class CountDownLatchTester implements Runnable {
    static final CountDownLatch latch = new CountDownLatch(10);
    @Override
    public void run() {
        // 检查任务
        try {
            System.out.println(Thread.currentThread().getName() + " 检查完毕!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            latch.countDown();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        for (int i = 10; i > 0; i--) {
            Thread.sleep(1000);
            executor.submit(new CountDownLatchTester());
            System.out.println(i);
        }

        Thread.sleep(1000);
        // 检查
        latch.await();

        System.out.println();
        System.out.println("点火,发射!");
        // 关闭线程池
        executor.shutdown();
    }
}

执行CountDownLatch的效果是:

 

 

 

二、Semaphore

Semaphore的功能如果用图来表示的话,就会是这样的:

 

 

 Semaphore实例代码:

/**
 * 信号量(摇号器)
 */
public class SemaphoreTester implements Runnable {
    static final Semaphore semaphore = new Semaphore(3);

    @Override
    public void run() {
        try {
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName() + " 开始进餐");
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        semaphore.release();
    }

    public static void main(String[] args) {
        ExecutorService excutor = Executors.newFixedThreadPool(15);
        for (int i = 0; i < 15; i++) {
            excutor.submit(new SemaphoreTester());
        }
        excutor.shutdown();
    }
}

Semaphore执行后的效果是:

 

 

三、CyclicBarrier

CyclicBarrier的功能如果用图来表示的话,就会是这样的:

 

 CyclicBarrier实例代码:

/**
 * 栅栏
 */
public class CyclicBarrierTester implements Runnable {
    private final static CyclicBarrier barrier = new CyclicBarrier(3);

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + " 已达到预定位置,等待指令...");
            // 只有最后一个线程执行后,所有的线程才能执行2
            barrier.await();
            Thread.sleep(1000);
            // 2 所有线程都会执行的动作
            System.out.println(Thread.currentThread().getName() + " 已突破第一道封锁线");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 15; i++) {
            executor.submit(new CyclicBarrierTester());
        }
        // 关闭线程池
        executor.shutdown();
    }
}

CyclicBarrier执行后的效果是:

四、Exchanger

Exchanger的功能如果用图来表示的话,就会是这样的:

 

 

 Exchanger实例代码:

/**
 * 交换机
 */
public class ExchangerTester implements Runnable {
    Exchanger<Object> exchanger = null;
    Object object = null;

    public ExchangerTester(Exchanger<Object> exchanger, Object object) {
        this.exchanger = exchanger;
        this.object = object;
    }

    @Override
    public void run() {
        try {
            Object previous = this.object;
            this.object = this.exchanger.exchange(this.object);
            System.out.println(Thread.currentThread().getName() + " 用对象 " + previous + " 换对象 " + this.object);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Exchanger<Object> exchanger = new Exchanger<Object>();
        ExchangerTester tester1 = new ExchangerTester(exchanger, "A");
        ExchangerTester tester2 = new ExchangerTester(exchanger, "B");

        new Thread(tester1).start();
        new Thread(tester2).start();
    }
}

Exchanger执行后的效果是:

 

 

 把这四种同步器掌握好(包括它们的组合使用),几乎可以解决90%以上的使用多线程的场景问题,再也不用担心不会多线程了。

与Java多线程-JUC-1(八)相似的内容:

Java多线程-JUC-1(八)

前面把线程相关的生命周期、关键字、线程池(ThreadPool)、ThreadLocal、CAS、锁和AQS都讲完了,现在就剩下怎么来用多线程了。而要想用好多线程,其实是可以取一些巧的,比如JUC(好多面试官喜欢问的JUC,就是现在要讲的JUC)。JUC就是java.util.concurrent的

Java多线程

一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程各重要知识点。掌握了上图中的各知识点,Java中的多线程也就基本上掌握了。主要包括: Java线程具有五中基本状态 新建状态(New):当线程对象对创建后,即进入了新建

杰哥教你面试之一百问系列:java多线程

java多线程是java面试中的高频问题,如何才能在面试中脱颖而出呢?熟读这里的一百个java多线程面试问题即可。 ### 1. **什么是线程?什么是进程?** **回答:** - 线程是操作系统能够进行调度的最小执行单位,它包含在进程中,共享进程的资源。 - 进程是一个正在执行中的程序,它包含了

JAVA多线程并发编程-避坑指南

本篇旨在基于编码规范、工作中积累的研发经验等,整理在多线程开发的过程中需要注意的部分,比如不考虑线程池参数、线程安全、死锁等问题,将会存在潜在极大的风险。并且对其进行根因分析,避免每天踩一坑,坑坑不一样。

Java多线程-线程生命周期(一)

如果要问我Java当中最难的部分是什么?最有意思的部分是什么?最多人讨论的部分是什么?那我会毫不犹豫地说:多线程。 Java多线程说它难,也不难,就是有点绕;说它简单,也不简单,需要理解的概念很多,尤其是很多底层知识,如数据结构、操作系统的部分。 Java多线程掌握得好,不仅仅只是对Java,对任何

Java多线程-线程关键字(二)

Java中和线程相关的关键字就两:volatile和synchronized。 volatile以前用得较少,以后会用得更少(后面解释)。它是一种非常轻量级的同步机制,它的三大特性是: 1、保证可见性,即强制将CPU高速缓存的数据立即写入主存,会导致其他CPU核中对应的高速缓存内容无效,就像这样:

Java多线程-ThreadPool线程池-1(三)

开完一趟车完整的过程是启动、行驶和停车,但老司机都知道,真正费油的不是行驶,而是长时间的怠速、频繁地踩刹车等动作。因为在速度切换的过程中,发送机要多做一些工作,当然就要多费一些油。 而一个Java线程完整的生命周期就包括: 1、T1:创建(启动) 2、T2:运行(行驶) 3、T3:销毁(停车) 而T

Java多线程-ThreadPool线程池-2(四)

线程池是个神器,用得好会非常地方便。本来觉得线程池的构造器有些复杂,即使讲清楚了对今后的用处可能也不太大,因为有一些Java定义好的线程池可以直接使用。但是(凡事总有个但是),还是觉得讲一讲可能跟有助于理解后面的常用线程池,所以该打脸还是打吧 因为直接结合代码看会更清楚一些,所以我把带注释的代码贴出

Java多线程-ThreadPool线程池-3(五)

除了可以通过ThreadPoolExecutor自定义线程池外,同Stream API中的Collectors一样,多线程里的Executors类也提供了一组相关的线程池工具,可以直接拿来用,不用考虑用什么队列合适的问题。 Javac除了传统的四大线程池工具: 1、newFixedThreadPoo

Java多线程-ThreadLocal(六)

为了提高CPU的利用率,工程师们创造了多线程。但是线程们说:要有光!(为了减少线程创建(T1启动)和销毁(T3切换)的时间),于是工程师们又接着创造了线程池ThreadPool。就这样就可以了吗?——不,工程师们并不满足于此,他们不把自己创造出来的线程给扒个底朝天决不罢手。 有了线程关键字解决线程安