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. go语言之进阶篇同名字段

    1.同名字段 示例: package main import "fmt" type Person struct { name string //名字 sex byte //性别, ...

  2. go语言基础之切片和底层数组关系

    1.切片和底层数组关系 示例: package main //必须有个main包 import "fmt" func main() { a := []int{0, 1, 2, 3, ...

  3. java获取指定文件夹下的所有文件名

    package com.henu.util; import java.io.File; public class TakeFilePathAndName { public static void ma ...

  4. Java基础(一):简介

    一.java基础语法: 一个Java程序可以认为是一系列对象的集合,而这些对象通过调用彼此的方法来协同工作.下面简要介绍下类.对象.方法和实例变量的概念. 对象:对象是类的一个实例,有状态和行为.例如 ...

  5. 解决PHP在Windows IIS 上传的图片无法访问的问题

    最近在做一个网站项目遇到了一个很奇怪的问题,现记录下来希望可以帮助到其他的朋友   问题描述: 最近公司刚刚在香港购买了一个Windows Server 2008 服务器用于将一个客户的N个php网站 ...

  6. json字符串和dict互转

    json字符串和dict互转 import json str = '{"params":[{"id":222,"offset":0},{&q ...

  7. 【C/C++】:用C实现输出日期的阴历日子

    前言 输出阴历一直是个老大难的问题.由于阴历日子没有规律.所以这里须要做的就是通过打表的算法做到输出阴历日子,可是非常多人都不太了解原理,我这里就给大家送上了一个福利.把自己做好的基于打表的阴历的日子 ...

  8. C++中无法解析的外部符号错误

      在编译C++程序的时候,如果引用了对应的头文件,但是调用一个函数的时候仍然出现" 无法解析的外部符号错误"的编译错误,比如: 无法解析的外部符号__imp__PathFileE ...

  9. JavaScript 之 日常积累

    1. <a>标签"加入收藏",兼容IE,FireFox等 function bookmarksite() { if (window.sidebar) { // Mozi ...

  10. (C++)UrlEncode的标准实现

    http://blog.163.com/xiaopengyan_109/blog/static/149832173201072685539394/ 地址栏传中文参数后是什么编码格式 网页中的表单使用P ...