您的位置:首页 > 其它

线程池的创建以及CyclicBarrier与CountDownLatch的简单使用

2017-10-26 00:00 573 查看

一、线程池的简单创建

(1)、使用Executors进行创建

ExecutorService poo1 = Executors.newFixedThreadPool(10);

ExecutorService pool = Executors.newSingleThreadExecutor();

这两种线程池都是无界队列的线程池,创建比较简单,但可能导致堆积请求处理队列而消耗非常大的内存。

ExecutorService poo1 = Executors.newCachedThreadPool();

这种可缓存线程池,创建无脑暴力,可以创建Integer.MAX_VALUE个线程,如果处理不当对内存消耗巨大。

ExecutorService poo1 = Executors.newScheduledThreadPool(10);

这是支持定时及周期性任务执行的一种线程池,不过同样会有上面的问题。

(2)、通过ThreadPoolExecutor创建线程池

ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("这是线程%d - ").build();

ExecutorService pool = new ThreadPoolExecutor(3, 6, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(3), threadFactory, new ThreadPoolExecutor.AbortPolicy());

以上是一个简单的实例,表示创建一个核心线程数为3,最大线程数为6,超时0s就进行回收,缓存队列容量为3,并使用AbortPolicy的拒绝策略

AbortPolicy         -- 当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException 异常。
CallerRunsPolicy    -- 当任务添加到线程池中被拒绝时,会在线程池当前正在运行的Thread线程池中处理被拒绝的任务。
DiscardOldestPolicy -- 当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。
DiscardPolicy       -- 当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务。

(3)、简单玩一玩这个线程池

public static void main(String[] args) {
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("这是线程%d - ").build();

ExecutorService pool = new ThreadPoolExecutor(3, 6, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(3), threadFactory, new ThreadPoolExecutor.AbortPolicy());

for (int i = 0; i < 10; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
Integer sout = i;
new Thread(() -> {
pool.submit(() -> {
System.out.println(Thread.currentThread().getName() + sout);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}).start();
}
}

打印:

这是线程0 - 0
这是线程1 - 1
这是线程2 - 2
这是线程3 - 6
这是线程4 - 7
这是线程5 - 8
Exception in thread "Thread-9" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@49984548 rejected from java.util.concurrent.ThreadPoolExecutor@475fb20b[Running, pool size = 6, active
7fe0
threads = 6, queued tasks = 3, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at com.example.demo.concurrentutil.TestCorrent.lambda$main$1(TestCorrent.java:28)
at java.lang.Thread.run(Thread.java:745)
这是线程0 - 3
这是线程1 - 4
这是线程2 - 5

可以看出, 最后一个线程(i = 9)被拒绝了,其他的线程都得到了执行,虽然我们指定核心线程池为3,但还是先执行了6个线程(最大线程),等待了2秒后,再执行被放入队列中的线程。

从上面打印的顺序,我们也能看出蹊跷,它并不是按照0 - 8的顺序来进行打印的,这是因为新的线程进入线程池后,如果超过了核心线程池大小,并不会去直接开启新的线程,而是先放入队列,直到队列满了,才会开启新的线程。

我们把maximumPoolSize从6改为4,会发生什么呢?

这是线程0 - 0
这是线程1 - 1
这是线程2 - 2
这是线程3 - 6
Exception in thread "Thread-7" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@1abd1e5 rejected from java.util.concurrent.ThreadPoolExecutor@f6c2fa0[Running, pool size = 4, active threads = 4, queued tasks = 3, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at com.example.demo.concurrentutil.TestCorrent.lambda$main$1(TestCorrent.java:28)
at java.lang.Thread.run(Thread.java:745)
Exception in thread "Thread-8" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@1a371600 rejected from java.util.concurrent.ThreadPoolExecutor@f6c2fa0[Running, pool size = 4, active threads = 4, queued tasks = 3, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at com.example.demo.concurrentutil.TestCorrent.lambda$main$1(TestCorrent.java:28)
at java.lang.Thread.run(Thread.java:745)
Exception in thread "Thread-9" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@317d0b0e rejected from java.util.concurrent.ThreadPoolExecutor@f6c2fa0[Running, pool size = 4, active threads = 4, queued tasks = 3, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at com.example.demo.concurrentutil.TestCorrent.lambda$main$1(TestCorrent.java:28)
at java.lang.Thread.run(Thread.java:745)
这是线程0 - 3
这是线程1 - 4
这是线程2 - 5

很显然,只有7个线程得到了执行。

再玩玩拒绝策略,比如 CallerRunsPolicy

ExecutorService pool = new ThreadPoolExecutor(3, 4, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(3), threadFactory, new ThreadPoolExecutor.CallerRunsPolicy());

打印:

这是线程0 - 0
这是线程1 - 1
这是线程2 - 2
这是线程3 - 6
Thread-77
Thread-88
Thread-99
这是线程0 - 3
这是线程1 - 4
这是线程2 - 5

上面被拒绝的三个线程,扔回给了它的调用方执行。

再比如 DiscardOldestPolicy 拒绝策略

这是线程0 - 0
这是线程1 - 1
这是线程2 - 2
这是线程3 - 6
这是线程0 - 7
这是线程1 - 8
这是线程2 - 9

直接将等待队列最末尾的任务放弃掉了,也就是先进入队列的345任务,被线程池放弃了。

最后一种 DiscardPolicy 就不赘述了,直接放弃被拒绝的任务。

二、简单用用 CountDownLatch

正如每个Java文档所描述的那样,CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行。在Java并发中,countdownlatch的概念是一个常见的面试题,所以一定要确保你很好的理解了它。

CountDownLatch countDownLatch = new CountDownLatch(2);

pool.submit(new Thread(() -> {
try {
sout("进入线程");
countDownLatch.await();
sout("继续执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}));

pool.submit(new Thread(() -> {
try {
Thread.sleep(7000);
sout("进入线程");
countDownLatch.await();
sout("继续执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}));

for (int i = 0; i < 2; i++) {
pool.submit(new Thread(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
sout("countDown");
}));
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

pool.shutdown();

这里有四个线程,一个 需要同步的线程 先开始执行,五秒后,countDown,七秒后,另一个 需要同步的线程 也开始执行,十秒后,再次countDown。

这是线程0 - 进入线程
这是线程2 - countDown
这是线程1 - 进入线程
这是线程3 - countDown
这是线程0 - 继续执行
这是线程1 - 继续执行

在线程3进行countDown后,线程0和线程1会从等待中恢复,并执行任务。

三、简单用用 CyclicBarrier

CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。

CyclicBarrier cyclicBarrier = new CyclicBarrier(2);

pool.submit(new Thread(() -> {
try {
sout("进入线程");
try {
cyclicBarrier.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
sout("继续执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}));

pool.submit(new Thread(() -> {
try {
Thread.sleep(7000);
sout("进入线程");
try {
cyclicBarrier.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
sout("继续执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}));

CyclicBarrier的使用比CountDownLatch更加简单,它只需要简单使用cyclicBarrier.await();就可以了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: