Java并发编程

转自:http://www.cnblogs.com/dolphin0520/category/602384.html

第一个例子(没有阻塞主线程,会先输出over):

 package javathreaddemo;

 import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; public class ThreadPoolExecutorDemo {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5));
int length = 15;
for (int i = 0; i < length; i++) {
MyTask myTask = new MyTask(i);
executor.execute(myTask);
System.out.println("线程池中线程数目:" + executor.getPoolSize() + ",队列中等待执行的任务数目:" + executor.getQueue().size() + ",已执行玩别的任务数目:" + executor.getCompletedTaskCount());
}
executor.shutdown();
System.out.println("over");
}
} class MyTask implements Runnable {
private int taskNum; public MyTask(int num) {
this.taskNum = num;
} public void run() {
System.out.println("正在执行task " + taskNum);
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("task " + taskNum + "执行完毕");
}
}

运行结果:

正在执行task 0
线程池中线程数目:1,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
线程池中线程数目:2,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 1
线程池中线程数目:3,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 2
线程池中线程数目:4,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 3
线程池中线程数目:5,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 4
线程池中线程数目:5,队列中等待执行的任务数目:1,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:2,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:3,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:4,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
线程池中线程数目:6,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
正在执行task 10
线程池中线程数目:7,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
正在执行task 11
线程池中线程数目:8,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
正在执行task 12
线程池中线程数目:9,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
正在执行task 13
线程池中线程数目:10,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
正在执行task 14
over

task 4执行完毕
task 2执行完毕
正在执行task 5
task 1执行完毕
task 3执行完毕
正在执行task 7
task 0执行完毕
正在执行task 6
task 10执行完毕
task 11执行完毕
task 12执行完毕
正在执行task 9
正在执行task 8
task 14执行完毕
task 13执行完毕
task 5执行完毕
task 7执行完毕
task 8执行完毕
task 6执行完毕
task 9执行完毕

如果第10行,改成 length > 15的值,程序运行就会报错。

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task javathreaddemo.MyTask@2cc7d960 rejected from java.util.concurrent.ThreadPoolExecutor@74904497[Running, pool size = 10, active threads = 10, queued tasks = 5, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2048)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:821)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1372)
at javathreaddemo.ThreadPoolExecutorDemo.main(ThreadPoolExecutorDemo.java:17)

原因是任务拒绝策略

  当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
第9行的代码创建线程池对象代码为:new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5));查看源码它里面使用的是AbortPolicy这个拒绝策略。
本以为这样就结束了,其实后来我发现真正的原因是因为使用了ArrayBlockingQueue<Runnable>(5)限制了队列里容量只能存放5个长度,如果将5改成10,和第二参数(maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程)一样就可以了,如果该值低于第二个参数值也会出现上面的错误。
总结一下:这种方式需要首先判断如果 (第一个参数 + 第二个参数) >= length,ArrayBlockingQueue<Runnable>(capacity)的capacity设置为第一个参数就可以了;如果 (第一个参数 + 第二个参数) < length , ArrayBlockingQueue<Runnable>(capacity)的capacity设置为第二个参数就可以了。
还有一种方式就是将new ArrayBlockingQueue<Runnable>(capacity)换成new LinkedBlockingQueue<Runnable>()对象就可以了,因为LinkedBlockingQueue有无参的构造函数,这样就不用管capacity和length到底应该设置成什么了。
第二个例子(增加阻塞主线程,等程序执行完成之后在输出over):
 package javathreaddemo;

 import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; public class ThreadPoolExecutorCountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5));
int length = 15;
final CountDownLatch latch = new CountDownLatch(length);
for (int i = 0; i < length; i++) {
final int index = i;
executor.execute(new Runnable() {
public void run() {
System.out.println("正在执行task " + index);
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("task " + index + "执行完毕");
 latch.countDown();
}
}
});
System.out.println("线程池中线程数目:" + executor.getPoolSize() + ",队列中等待执行的任务数目:" + executor.getQueue().size() + ",已执行玩别的任务数目:" + executor.getCompletedTaskCount());
}
// 使用CountDownLatch确保所有线程结束后才往下走
latch.await();
executor.shutdown();
System.out.println("over");
}
}

运行结果:
正在执行task 0
线程池中线程数目:1,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
线程池中线程数目:2,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
线程池中线程数目:3,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 1
正在执行task 2
线程池中线程数目:4,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 3
线程池中线程数目:5,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:1,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:2,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:3,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:4,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
正在执行task 4
线程池中线程数目:6,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
正在执行task 10
线程池中线程数目:7,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
正在执行task 11
线程池中线程数目:8,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
正在执行task 12
线程池中线程数目:9,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
正在执行task 13
线程池中线程数目:10,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
正在执行task 14
task 0执行完毕
正在执行task 5
task 2执行完毕
task 1执行完毕
task 10执行完毕
task 4执行完毕
正在执行task 9
正在执行task 8
正在执行task 7
task 3执行完毕
正在执行task 6
task 11执行完毕
task 12执行完毕
task 14执行完毕
task 13执行完毕
task 5执行完毕
task 7执行完毕
task 8执行完毕
task 9执行完毕
task 6执行完毕
over

不过在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池:
Executors.newCachedThreadPool();        //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE
Executors.newSingleThreadExecutor(); //创建容量为1的缓冲池
Executors.newFixedThreadPool(int); //创建固定容量大小的缓冲池

下面是这三个静态方法的具体实现;
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}

从它们的具体实现来看,它们实际上也是调用了ThreadPoolExecutor,只不过参数都已配置好了。
newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;
newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1,也使用的LinkedBlockingQueue;
newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为 Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。
实际中,如果Executors提供的三个静态方法能满足要求,就尽量使用它提供的三个方法,因为自己去手动配置ThreadPoolExecutor的参数有点麻烦,要根据实际任务的类型和数量来进行配置。
使用Executors创建线程池:
 package javathreaddemo;

 import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor; public class ExecutorsDemo {
public static void main(String[] args) throws InterruptedException {
int count = 5;
ThreadPoolExecutor executor = (ThreadPoolExecutor)Executors.newFixedThreadPool(count);
int length = 15;
final CountDownLatch latch = new CountDownLatch(length);
for (int i = 0; i < length; i++) {
final int index = i;
executor.execute(new Runnable() {
public void run() {
System.out.println("正在执行task " + index);
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("task " + index + "执行完毕");
latch.countDown();
}
}
});
System.out.println("线程池中线程数目:" + executor.getPoolSize() + ",队列中等待执行的任务数目:" + executor.getQueue().size() + ",已执行玩别的任务数目:" + executor.getCompletedTaskCount());
}
// 使用CountDownLatch确保所有线程结束后才往下走
latch.await();
executor.shutdown();
System.out.println("over");
}
}

运行结果:


正在执行task 0
线程池中线程数目:1,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
线程池中线程数目:2,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 1
线程池中线程数目:3,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 2
线程池中线程数目:4,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 3
线程池中线程数目:5,队列中等待执行的任务数目:0,已执行玩别的任务数目:0
正在执行task 4
线程池中线程数目:5,队列中等待执行的任务数目:1,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:2,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:3,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:4,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:5,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:6,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:7,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:8,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:9,已执行玩别的任务数目:0
线程池中线程数目:5,队列中等待执行的任务数目:10,已执行玩别的任务数目:0
task 2执行完毕
task 0执行完毕
task 3执行完毕
task 1执行完毕
正在执行task 7
正在执行task 6
正在执行task 5
task 4执行完毕
正在执行task 8
正在执行task 9
task 9执行完毕
task 6执行完毕
task 8执行完毕
task 5执行完毕
task 7执行完毕
正在执行task 13
正在执行task 12
正在执行task 11
正在执行task 10
正在执行task 14
task 13执行完毕
task 10执行完毕
task 11执行完毕
task 12执行完毕
task 14执行完毕
over

使用Executors还有一个好处就是你不需要考虑count和length到底应该设置什么才不会出错,因为Executors.newFixedThreadPool里面使用的是LinkedBlockingQueue<Runnable>这个对象。
 

Java并发编程,多线程[转]的更多相关文章

  1. Java并发编程(多线程)中的相关概念

    众所周知,在Java的知识体系中,并发编程是非常重要的一环,也是面试中必问的题,一个好的Java程序员是必须对并发编程这块有所了解的. 并发必须知道的概念 在深入学习并发编程之前,我们需要了解几个基本 ...

  2. Java并发编程--多线程中的join方法详解

    Java Thread中, join()方法主要是让调用该方法的thread在完成run方法里面的部分后, 再执行join()方法后面的代码 例如:定义一个People类,run方法是输出姓名年龄. ...

  3. Java并发编程、多线程、线程池…

    <实战java高并发程序设计>源码整理https://github.com/petercao/concurrent-programming/blob/master/README.md Ja ...

  4. java并发编程笔记(九)——多线程并发最佳实践

    java并发编程笔记(九)--多线程并发最佳实践 使用本地变量 使用不可变类 最小化锁的作用域范围 使用线程池Executor,而不是直接new Thread执行 宁可使用同步也不要使用线程的wait ...

  5. 多线程(一)java并发编程基础知识

    线程的应用 如何应用多线程 在 Java 中,有多种方式来实现多线程.继承 Thread 类.实现 Runnable 接口.使用 ExecutorService.Callable.Future 实现带 ...

  6. 【多线程】Java并发编程:Lock(转载)

    原文链接:http://www.cnblogs.com/dolphin0520/p/3923167.html Java并发编程:Lock 在上一篇文章中我们讲到了如何使用关键字synchronized ...

  7. Java并发编程-JUC-CountDownLatch 倒计数门闩器-等待多线程完成再放行 -一次性使用

    如题 (总结要点) CountDownLatch 倒计数门闩器, 让1-n-1个线程等待其他多线程完成工作. (Excel的多个Sheet的解析,最终等待解析完毕后;要实现主线程等待所有线程完成she ...

  8. 【Java并发编程实战】-----“J.U.C”:CyclicBarrier

    在上篇博客([Java并发编程实战]-----"J.U.C":Semaphore)中,LZ介绍了Semaphore,下面LZ介绍CyclicBarrier.在JDK API中是这么 ...

  9. 【Java并发编程实战】-----“J.U.C”:ReentrantReadWriteLock

    ReentrantLock实现了标准的互斥操作,也就是说在某一时刻只有有一个线程持有锁.ReentrantLock采用这种独占的保守锁直接,在一定程度上减低了吞吐量.在这种情况下任何的"读/ ...

随机推荐

  1. 在Java Web程序中使用Hibernate

    在Java Web程序中使用Hibernate与普通Java程序一样.本文中将使用Servlet和JSP结合Hibernate实现数据库表的增删改查操作. Web程序中,hibernate.cfg.x ...

  2. Git教程之工作区和暂存区

    工作区(Working Directory) 就是你在电脑里能看到的目录,比如我的learngit文件夹就是一个工作区:

  3. iOS开发-简单的图片查看器

    现在你只要拿着手机,不管你Android还是iOS,新闻类的App不可避免都有一个功能就是图片查看,做个专题,查看一下内容,App Store中也有专门针对图片浏览的App,鉴于目前所知有限,无法做到 ...

  4. JAVASCRIPT加密方法,JS加密解密综述(7种)

    一:最简单的加密解密 对于JAVASCRIPT函数escape()和unescape()想必是比较了解啦(很多网页加密在用它们),分别是编码和解码字符串,比如例子代码 用escape()函数加密后变为 ...

  5. 使用Git将本地项目上传到Github操作详解

    Git的安装就不说了. 一.建本地仓库 1.第一步:我们需要先创建一个本地的版本库(其实也就是一个文件夹). 你可以直接右击新建文件夹,也可以右击打开Git bash命令行窗口通过命令来创建. 反正就 ...

  6. WordPress 无法使用the_content()方法输出内容

    在使用WordPress里在一个页面里我使用the_content()方法来输出当前页面的内容,但却显示为空,而标题,url等都没有问题 在网络上好像遇到这种情况的人很少只找到了一个说是可能是func ...

  7. [Functional Programming] Build a Linear congruential generator

    What we are going to do in this post, is to build a random number generator. As you might know that ...

  8. [Node.js]30. Level 6: Listen 'Question' from client, and then Answer the Question

    Clients can also answer each other questions, so let's build that feature by first listening for the ...

  9. unity3d插件Daikon Forge GUI 中文教程-3-基础控件Button和Sprite的使用

    (游戏蛮牛首发)大家好我是孙广东.官网提供了专业的视频教程http://www.daikonforge.com/dfgui/tutorials/,只是是在youtube上.要观看是须要FQ的. 只是教 ...

  10. Graphical vi-vim Cheat Sheet and Tutorial

    主要内容: VIM编辑器的快捷命令cheat sheet(小抄),一共七张图,简单明了地介绍了vim的快捷命令,相比于满满的图表,分成七小块更易于记住. 更多关于vim的cheat sheet以及相关 ...