java核心技术36讲笔记 第19讲 Java并发包提供了哪些并发工具类?

Java并发包提供了哪些并发工具类?

我们通常所说的并发包也就是java.util.concurrent及其子包,集中了Java并发的各种基础工具类,具体主要包括几个方面:

  • 提供了比synchronized更加高级的各种同步结构,包括CountDownLatch、 CyclicBarrier、 Semaphore等,可以实现更加丰富的多线程操作,比如利用Semaphore作为资源
    控制器,限制同时进行工作的线程数量。
  • 各种线程安全的容器,比如最常见的ConcurrentHashMap、有序的ConcunrrentSkipListMap,或者通过类似快照机制,实现线程安全的动态数
    组CopyOnWriteArrayList等。
  • 各种并发队列实现,如各种BlockedQueue实现,比较典型的ArrayBlockingQueue、 SynchorousQueue或针对特定场景的PriorityBlockingQueue等
  • 强大的Executor框架,可以创建各种不同类型的线程池,调度任务运行等,绝大部分情况下,不再需要自己从头实现线程池和任务调度器。

从jdk文档中我们看到:
java8jdk在线
主要有3个包

java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks

这三个包中包含了上述的回答。
作者文章中主要关注了3个点,剩下的后面会有提到。

  • CountDownLatch,允许一个或多个线程等待某些操作完成
  • CyclicBarrier,一种辅助性的同步结构,允许多个线程等待到达某个屏障。
  • Semaphore, Java版本的信号量实现。

Semaphore

基本API

Semaphore(int permits):构造方法

Semaphore(int permits,boolean fair):构造方法,当fair等于true时,创建具有给定许可数的计数信号量并设置为公平信号量。

void acquire():从此信号量获取一个许可前线程将一直阻塞。或线程为 interrupted 。

acquire(int permits):每调用一次此方法,就获得permits个许可。

release():释放许可证,将其返回到信号量。

release(int permits):释放给定数量(permits)的许可证,将其返回到信号量。

int availablePermits():返回此信号量中当前可用的许可数。

Semaphore使用

4步

	//创建信号量
   Semaphore semaphore = new Semaphore(5);
   //创建线程
   new Thread(new SemaphoreWorker(semaphore));
   //获取
     semaphore.acquire();
     //释放
    semaphore.release();

Semaphore情景:

队伍一次进来5个人上车,等这5个人坐车出发,再放进去下一批

public class UsualSemaphoreSample {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("Action...GO!");
        Semaphore semaphore = new Semaphore(5);
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(new SemaphoreWorker(semaphore));
            t.start();
        }
    }
}
class SemaphoreWorker implements Runnable {
    private String name;
    private Semaphore semaphore;
    public SemaphoreWorker(Semaphore semaphore) {
        this.semaphore = semaphore;
    }
    @Override
    public void run() {
        try {
            log("is waiting for a permit!");
            semaphore.acquire();
            log("acquired a permit!");
            log("executed!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            log("released a permit!");
            semaphore.release();
        }
    }
    private void log(String msg){
        if (name == null) {
            name = Thread.currentThread().getName();
        }
        System.out.println(name + " " + msg);
    }
}

结果:

Action...GO!
Thread-0 is waiting for a permit!
Thread-2 is waiting for a permit!
Thread-1 is waiting for a permit!
Thread-1 acquired a permit!
Thread-1 executed!
Thread-1 released a permit!
Thread-0 acquired a permit!
Thread-0 executed!
Thread-2 acquired a permit!
Thread-0 released a permit!
Thread-3 is waiting for a permit!
Thread-2 executed!
Thread-2 released a permit!
Thread-3 acquired a permit!
Thread-3 executed!
Thread-3 released a permit!
Thread-5 is waiting for a permit!
Thread-5 acquired a permit!
Thread-5 executed!
Thread-5 released a permit!
Thread-6 is waiting for a permit!
Thread-6 acquired a permit!
Thread-6 executed!
Thread-6 released a permit!
Thread-4 is waiting for a permit!
Thread-4 acquired a permit!
Thread-4 executed!
Thread-4 released a permit!
Thread-7 is waiting for a permit!
Thread-7 acquired a permit!
Thread-7 executed!
Thread-7 released a permit!
Thread-8 is waiting for a permit!
Thread-8 acquired a permit!
Thread-8 executed!
Thread-8 released a permit!
Thread-9 is waiting for a permit!
Thread-9 acquired a permit!
Thread-9 executed!
Thread-9 released a permit!

Process finished with exit code 0

线程试图获得工作允许,得到许可则进行任务,然后释放许可,这时等待许可的其他线程,就可获得许可进入工作状态,直到全部处理结束。

缺点:
一直有5个人可以试图乘车,如果有1个人出发了,立即就有排队的人获得
许可,而这并不完全符合我们前面的要求

修改


public class AbnormalSemaphoreSample {
    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphore = new Semaphore(0);
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(new MyWorker(semaphore));
            t.start();
        }
        System.out.println("Action...GO!");
        semaphore.release(5);
        System.out.println("Wait for permits of");
        while (semaphore.availablePermits()!=0) {
            Thread.sleep(100L);
        }
        System.out.println("Action...GO again!");
        semaphore.release(5);
    }
}


class MyWorker implements Runnable {
    private Semaphore semaphore;
    public MyWorker(Semaphore semaphore) {
        this.semaphore = semaphore;
    }
    @Override
    public void run() {
        try {
            semaphore.acquire();
            System.out.println("Executed!"+ Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果:

Action...GO!
Wait for permits of
Executed!Thread-1
Executed!Thread-2
Executed!Thread-0
Executed!Thread-3
Executed!Thread-4
Action...GO again!
Executed!Thread-7
Executed!Thread-8
Executed!Thread-5
Executed!Thread-9
Executed!Thread-6

Process finished with exit code 0

CountDownLatch和CyclicBarrier的不同点

  • CountDownLatch是不可以重置的,所以无法重用;而CyclicBarrier则没有这种限制,可以重用
  • CountDownLatch的基本操作组合是countDown/await。调用await的线程阻塞等待countDown足够的次数,不管你是在一个线程还是多个线程里countDown,只要次数足够
    即可。所以就像Brain Goetz说过的, CountDownLatch操作的是事件。
  • CyclicBarrier的基本操作组合,则就是await,当所有的伙伴(parties)都调用了await,才会继续进行任务,并自动进行重置。 注意,正常情况下, CyclicBarrier的重置都是自
    动发生的,如果我们调用reset方法,但还有线程在等待,就会导致等待线程被打扰,抛出BrokenBarrierException异常。 CyclicBarrier侧重点是线程,而不是调用事件,它的
    典型应用场景是用来等待并发线程结束。

CountDownLatch说明

上面的区别作者说完之后,对于我从来没有用过的人来说还是不明白什么意思。

主要补充以下问题。

  • 判断count不为0的时,则当前线程呈wait状态。

  • await的线程阻塞等待countDown足够的次数(次数由构造方法提供),下面的次数为6。

//创建6个CountDownLatch类对象
 CountDownLatch latch = new CountDownLatch(6);
  • countDown()方法,此方法将CountDownLatch(6)中6 减1
  • await()方法 :当前线程等待,直到 减少到0.
  • getCount()方法:通常用来测试用的,当前计数的值。

再看下面的代码会好些。

CountDownLatch情景

假设有10个人排队,我们将其分成5个人一批,一共两个批次,通过CountDownLatch来协调批次。

import java.util.concurrent.CountDownLatch;

public class CountDownLatchSample {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(6);
        for (int i = 0; i < 5; i++) {
            Thread t = new Thread(new FirstBatchWorker(latch));
            t.start();
        }
        for (int i = 0; i < 5; i++) {
            Thread t = new Thread(new SecondBatchWorker(latch));
            t.start();
        }

        // 注意这里也是演示目的的逻辑,并不是推荐的协调方式
        while ( latch.getCount() != 1 ){
            Thread.sleep(100L);
        }
        System.out.println("Wait for first batch finish");
        latch.countDown();
    }
}
public class FirstBatchWorker implements Runnable {
    private CountDownLatch latch;

    public FirstBatchWorker(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        System.out.println("First batch executed!"+Thread.currentThread().getName());
        latch.countDown();
            //每次countDown之后都会少1
        System.out.println("First batch getCount"+ latch.getCount());
    }
}
import java.util.concurrent.CountDownLatch;

public class SecondBatchWorker implements Runnable {

    private CountDownLatch latch;

    public SecondBatchWorker(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            latch.await();
            System.out.println("Second batch executed!"+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果:

First batch executed!Thread-1
First batch getCount5
First batch executed!Thread-2
First batch executed!Thread-0
First batch getCount4
First batch getCount3
First batch executed!Thread-4
First batch getCount2
First batch executed!Thread-3
First batch getCount1
Wait for first batch finish
Second batch executed!Thread-8
Second batch executed!Thread-9
Second batch executed!Thread-5
Second batch executed!Thread-7
Second batch executed!Thread-6

Process finished with exit code 0

CyclicBarrier

作者说完之后还是不明白。。。自我总结如下:

  • 从单词上看,是循环,屏障的意思。不同点从字面上看出来了,CyclicBarrier是循环,CountDownLatch无法重用。

  • CyclicBarrier计数为加法。

  • 构造函数:CyclicBarrier(int parties, Runnable barrierAction)

    • parties:是参与线程的个数,不能小于1
    • Runnable :最后一个到达线程要做的任务
  • await()方法:线程调用 await() 表示自己已经到达Barrier

  • 下面代码5个线程都执行了await()方法都执行了,才执行下面的方法。否则线程彼此等待,一直呈阻塞状态。

        CyclicBarrier barrier = new CyclicBarrier(5, new Runnable() {
            @Override
            public void run() {
                System.out.println("Action...GO again!");
            }
        });

在这里插入图片描述

CyclicBarrier情景

5个工作线程其实更像是代表了5个可以就绪空车,而不再是5个乘客,对比前面CountDownLatch的例子更有助于我们区别它们的抽象模型。


public class CyclicBarrierSample {
    public static void main(String[] args) throws InterruptedException {
        CyclicBarrier barrier = new CyclicBarrier(5, new Runnable() {
            @Override
            public void run() {
            //5个线程执行了才会去执行此方法。
                System.out.println("Action...GO again!");
            }
        });
        //5个线程
        for (int i = 0; i < 5; i++) {
            Thread t = new Thread(new CyclicWorker(barrier));
            t.start();
        }
    }


    static class CyclicWorker implements Runnable {
        private CyclicBarrier barrier;

        public CyclicWorker(CyclicBarrier barrier) {
            this.barrier = barrier;
        }

        @Override
        public void run() {
            try {
            	//结果循环了3次 自动重置
                for (int i = 0; i < 3; i++) {
                    System.out.println(Thread.currentThread().getName()+" Executed!");
                    barrier.await();
                }

            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


结果:

Thread-1 Executed!
Thread-0 Executed!
Thread-2 Executed!
Thread-3 Executed!
Thread-4 Executed!
Action...GO again!
Thread-4 Executed!
Thread-2 Executed!
Thread-1 Executed!
Thread-3 Executed!
Thread-0 Executed!
Action...GO again!
Thread-0 Executed!
Thread-4 Executed!
Thread-1 Executed!
Thread-3 Executed!
Thread-2 Executed!
Action...GO again!
发布了93 篇原创文章 · 获赞 26 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/sxj159753/article/details/99655006