《Java并发编程(核心方法与框架)》笔记记录

此文记录学习《Java并发编程(核心方法与框架)》时的记录
代码见github:https://github.com/Bosssheep/JavaConcurrent

一、Semaphore

Semaphore,这个单词翻译过来指:信号
在Java中此类主要用来限制线程并发的数量
其常用方法如下:

方法 作用
Semaphore(int permits) 构造方法,permits表示同一时间最多允许多少个线程同时执行acquire()release()之间的代码
Semaphore() 无参构造。默认permits=1
acquire(int permits) 调用一次,就使用permits个许可,如果许可个数不够,则等待
acquireUninterruptibly() 使等待进方法的线程,不允许被中断,即Thread.interrupt()无效
release() 释放许可
availablePermits() 返回Semaphore对象中当前可用的许可,等于初始化的permits减去其他线程已获得的许可数
drainPermits() 获取并返回所有可用的许可个数,并且将许可置0
getQueueLength() 返回等待许可的线程个数
hasQueuedThread() 判断有没有线程在等待这个许可
tryAcquire() 尝试获取1个许可,获取不到则返回false
tryAcquire(int permits) 尝试获取多个许可,获取不到则返回false
tryAcquire(long timeout,TimeUnit unit) 在time内尝试获取许可,获取不到则返回false

Semaphore最常应用的一个模型的就是:生产者-消费者模型

二、CountDowntLatch

Latch,单词的意思使:门阀、门锁。所以当门锁没有打开时,N个线程不能继续向下运行,直到满足某条件时让线程以“组团”的方式一起执行任务。

类CountDownLatch提供的功能是:判断count计数不为0时,则当前线程呈wait等待状态,也就是在屏障处等待,当count计数为0时多个等待中的线程可以一起从wait中唤醒并且继续执行。

主要的方法有两个:await()countDown()
await() 判断计数是否为0,不为0则呈等待状态。其他线程可以调用 countDown() 将计数-1,当计数减为0时,等待的线程们继续运行。

三、CyclicBarrier

类CyclicBarrier不仅有CountDownLatch的功能,还可以实现阶段性同步,也就是循环地实现线程一起执行的目标,而不是像CountDownLatch一样仅支持一次线程与同步阻塞的特性。
它允许一组线程相互等待,直到到达某个公共屏障点。
与CountDownLatch不同的是,CyclicBarrier使用加法操作。

CyclicBarrier具有屏障重置性,这也是保证循环特性的一个体现。

常用的几个方法:

方法 作用
CyclicBarrier(int parties) 构造方法,parities表示到达屏障的条件为parties个线程
CyclicBarrier(int parties,runnable()) 构造方法,当经过第一道屏障时调用runnable方法
await() 直到parties个线程都调用了await()方法才继续往下执行
getNumberWaiting() 获得有几个线程已经达到屏障点
getParties() 获取Parties个数
reset() 重置屏障
isBroken() 查询此屏障是否处于损坏状态

对于屏障损坏状态的补充:
如果有一个线程由于中断或者超时而提前离开了屏障点,其他所有在屏障点等待的线程也会抛出BrokenBarrierException或者InterruptedException异常,并且离开屏障点,此时就会进入Broken状态。

四、Phaser

CyclicBarrier类有一些缺陷,如不可以动态添加parties计数,调用await()仅占用1个parties。而Phaser都能解决这些问题。

Phaser类具有CyclicBarrier的功能,此外还提供动态增减parites计数,通过若干个方法来控制多个线程之间同步运行的效果,还可以实现针对某一个线程取消同步运行的效果,而且支持在指定屏障处等待、在等待时支持中断或非中断等功能。功能更加强大。

以下是Phaser常见的方法:

方法 功能
Phaser(int) 指定parties个数
arriveAndAwaitAdvance() 当前线程已经到达屏障,当计数不足时,呈阻塞状态,等条件满足后继续向下一个屏障执行
arriveAndDeregister() 使当前线程退出比赛,并且使parties值-1
getPhase() 获取已经到达第几个屏障
onAdvance() 通过新的屏障时被调用
getRegisteredParties() 获得注册的parites数量
register() 动态添加一个parties值
bulkRegister() 批量增加parties数量
getArrivedParties() 获得已经被使用的parties值
getUnarrivedParites() 获得未被使用的parties值
arrive() 使arrived parties数+1,并且不在屏障处等待,直接向下继续执行,如果经过屏障,同样具有计数重置功能;计数不足使,其他线程依然等待
awaiAdvance(int phase) 如果传入参数phase值和当前getPhase方法返回值一样,则在屏障处,否则继续向下执行;不可中断
awaitAdvanceInterruptibly(int) 同上,可中断
forceTermination() 使Phaser对象的屏障功能失效
isTermination() 判断是否实现

五、Executor

https://www.cnblogs.com/fengsehng/p/6048610.html

下面整理了一下常见接口和类的关系。
(一)Executor
Executor是接口,与线程池有关的大部分类都是实现该接口。
(二)ExecutorService
ExecutorService是Executor的子接口,其唯一子实现类是AbstractExecutorService(抽象类)。
(三)ThreadPoolExecutor
ThreadPoolExecutor是AbstractExecutorService的子类,可以进行实例化。

构造函数如下:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
   //BlockingQueue可以用:LinkedBlockingQueue,SynchronousQueue

参数的各种情况:
1、当 任务数 < corePoolSize,马上创建线程运行这个任务,不放入扩展队列中
2、当 任务数 > corePoolSize && 使用LinkedBlockingQueue, maximumPoolSize和keepAliveTime参数忽略,并把任务放入LinkedBlockingQueue中等待被执行
3、当 任务数 > corePoolSize && 任务数 <= maximumPoolSize && 使用SynchronousQueue, maximumPoolSize和keepAliveTime参数有效。马上创建非核心线程执行那些超过corePoolSize的任务,并且任务完成后在keepAliveTime后将非核心线程清除
4、当 任务数 > corePoolSize && 任务数 > maximumPoolSize && 使用SynchronousQueue, maximumPoolSize和keepAliveTime参数有效。当执行超过maximumPoolSize将会抛异常

拒绝策略:
线程池的资源全部被占用的时候,对新添加的Task有不同处理策略。
拒绝策略由defaultHandler参数指定。
A、AbortPolicy:抛出RejectedExecutionException
B、CallerRunsPolicy:使用调用线程池的Thread线程去处理被拒绝的任务
C、DiscardOldestPolicy:放弃等待队列中最旧的未处理任务。将被拒绝的任务添加进去
D:DiscardPolicy:直接丢弃任务

(四)Executors
Executors是工厂类,可通过该接口创建指定类型的线程池
内部实现都是new一个ThreadPoolExecutor ,通过不同参数控制创建不同类型的线程池

方法 线程池名称 介绍 Core Max AliveTime BlockingQueue
newCachaedThreadPool() 无界线程池 进行线程池自动回收,池中存放线程个数为Integer.MAX_VALUE 0 Integer.MAX_VALUE 60L SynchronousQueue
newFixedThreadPool(int nThreads) 有界线程池 固定最大线程数,超过则放入队列中等待 nThreads nThreads 0L LinkedBlockingQueue
newSingleThreadExecutor() 单一线程池 以队列形式执行任务 1 1 0L LinkedBlockingQueue

六、Future&&Callable

默认情况下,Thread对象不具有返回值的功能,但是结合Future和Callable,可以让线程具有返回值的功能
示例代码如下:

import java.util.concurrent.*;

/**
 * ExecutorService的submit(Callable)与Future的get方法配合使用
 * Future.get()方法具有阻塞特性
 * submit() VS execute():
 *          execute没有返回值,submit具有返回值。
 *          execute出现异常直接打印堆栈信息,submit可以捕获;通过线程池设置UncaughtExceptionHandler,执行execute也可以捕捉异常
 */
class  MyCallable implements Callable{
    private int age;

    public MyCallable(int age){
        super();
        this.age = age;
    }
    @Override
    public String call() throws Exception {
        Thread.sleep(3000);
        return "返回值 年龄是"+age;
    }
}
class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("打印信息");
    }
}
public class Test {


    public static void main(String[] args) {
        try{
            //创建一个自定义Callable类
            MyCallable callable = new MyCallable(100);
            //创建一个ThreadPoolExecutor
            ThreadPoolExecutor executor = new ThreadPoolExecutor(2,3,5L, TimeUnit.SECONDS,new LinkedBlockingDeque<>());

            //1、将Callable作为参数传入,打开线程执行Callable重写的Call()方法,并且返回类型为Future
            System.out.println("----1----");
            Future future = executor.submit(callable);
            //通过Future的get方法获取任务返回值,此方法阻塞
            System.out.println(future.get());


            //2、将Runnable传入submit()方法
            System.out.println("----2----");
            Future future2 = executor.submit(new MyRunnable());
            //如果submit传入的是Runnable
            System.out.println(future2.get() + ","+future.isDone());


            //3、将Runnable和result参数传入submit,result可以作为执行结果的返回值,替代Future的get方法
            System.out.println("----3----");
            UserInfo userInfo = new UserInfo();
            Future future3 = executor.submit(new SetUserInfoRunnable(userInfo),userInfo);
            userInfo = future3.get();
            System.out.println("结果:"+userInfo.getUsername()+","+userInfo.getPassword());

        }catch (Exception e){

        }
    }
}

七、CompletionService

CompletionService可以以异步的方式一边产生新任务,一边处理已完成任务。
解决了Future的阻塞的缺点。

八、ScheduledExecutorService

ScheduledExecutorService可以将定时任务和线程池功能结合起来,让任务以周期形式执行。

九、Fork-Join

使用RecursiveAction、RecursiveTask分解任务,合并任务结果。

本文具体代码:
https://github.com/Bosssheep/JavaConcurrent/tree/master/out/production/JavaConcurrent

你可能感兴趣的:(java,并发编程)