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. 怎样在centos安装python-mysql?

    在python中使用mysql,须要安装mysql-python依赖包, 能够通过pip来安装: pip install MySQL-python 假设错误发生,须要先安装一个开发包: yum ins ...

  2. OpenCV学习(34) 点到轮廓的距离

    在OpenCV中,可以很方便的计算一个像素点到轮廓的距离,计算距离的函数为: double pointPolygonTest(InputArray contour, Point2f pt, bool ...

  3. Python并发编程-Memcached (分布式内存对象缓存系统)

    一.Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的 ...

  4. Word Break leetcode java

    题目: Given a string s and a dictionary of words dict, determine if s can be segmented into a space-se ...

  5. Android制作曲线、柱状图、饼形等图表——使用AChartEngine

    之前在java开发中实现图表使用JFreeChar组件,最近有个小项目要求在Android端进行数据分析,如何实现图表呢?查了一下google提供了一个开源组件Achartengine非常好用,可实现 ...

  6. [Javascript] Funciton Expression

    //This will load the code into the memory no matter //you call it or not function diffOfSquares(a,b) ...

  7. 用Java发送HTML格式邮件测试类(支持中文)

    代码由纯Java写成,支持中文,一目了然,只要将Main函数中的相关信息填写正确就直接用了,便于修改,可以在此类基础上任意扩展成自己的类. 注意做HTML形式的邮件,最好把HTML,CSS都写全,只写 ...

  8. 压缩 MongoDB 的数据文件

    MongoDB采用了磁盘空间预分配的机制,为了避免磁盘碎片以及使用mmap后造成的近一步的内存碎片,但是随着数据的增删除改操作,数据文件不可避免的会产生空洞,造成磁盘空间和内存的浪费.本文说的是这方面 ...

  9. 打开mdb文件0x80040213

    在操作数据库时,有时遇到 0x80040213的错误,这往往是路径的问题,即所操作的文件.图层.字段等不存在. 在<ArcGIS Engine地理信息系统开发从入门到精通>一书中也谈到了这 ...

  10. JSTL详解(二)

    JSTL详解(二) 在JSTL中,一般用途的标签只要是指具有输出,设置变量,和错误处理等功能的标签,他们在jsp中使用很频繁,它们有: l         <c:out> l        ...