在了解线程池之前,首先我们先了解什么是线程
在JAVA中,有两种方式创建线程
1、实现Runnable接口,实例化Thread;2 、继承Thread类,重写run()方法
区别:
Thread需要实现Runnable ,Runnable没有start()方法,只有Thread类中才有
(该段摘自百度)
http://wenku.baidu.com/link?url=Nxc165sfKCfnRuCnkZnNGNfF5ReG0tHU5cuYf-nrwhXnt-dX7ANAri-sjFUR_XfENJd-zBzKqIUvy5axb6V--vv8akTUV6AffaqGR5Xg1BC
在实际开发中一个多线程的操作很少使用Thread类,而是通过Runnable接口完成。
1、避免点继承的局限,一个类可以继承多个接口。2、适合于资源的共享
以常见的卖票为例
A: 通过Thread 类完成:
首先,定义一个窗口用于卖票的线程
public class MyThread extends Thread{
private int count =10;
private String name ;
public MyThread(String name){
this.name = name;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i = 0;i<20;i++){
//i 表示该窗口卖出的张数对应的人的位数
if(this.count>0){
System.out.println(this.name+"買到第:"+count--+"張票");
}
}
}
}
public class TicketThread {
public static void main(String[] args) {
MyThread m1 = new MyThread("one");
MyThread m2 = new MyThread("two");
MyThread m3 = new MyThread("three");
m1.start();
m2.start();
m3.start();
}
}
三个线程开启后,控制台输出的结果如下:
two買到第:10張票
three買到第:10張票
one買到第:10張票
three買到第:9張票
two買到第:9張票
three買到第:8張票
one買到第:9張票
three買到第:7張票
two買到第:8張票
three買到第:6張票
one買到第:8張票
one買到第:7張票
three買到第:5張票
two買到第:7張票
three買到第:4張票
one買到第:6張票
three買到第:3張票
two買到第:6張票
three買到第:2張票
one買到第:5張票
three買到第:1張票
two買到第:5張票
one買到第:4張票
two買到第:4張票
one買到第:3張票
two買到第:3張票
one買到第:2張票
two買到第:2張票
one買到第:1張票
two買到第:1張票
很明显,三个线程都默认10张票归自己所有,当三个线程开启后,一个卖出了30张票,线程与线程之间并没有进行数据的交互
现在我们实现第二种方案:实现Runnable 接口
同样的,让我们的MyThread 实现Runnable 接口
public class MyThread implements Runnable{
private int count = 10;
@Override
public void run() {
// TODO Auto-generated method stub
for(int i = 10;i<20;i++){
synchronized (this)//同步
{
if(this.count>0){
System.out.println("買到第:"+count--+"張票");
}
}
}
}
}
public class TicketThread {
public static void main(String[] args) {
MyThread thread = new MyThread();
new Thread(thread).start();
new Thread(thread).start();
new Thread(thread).start();
}
}
買到第:10張票
買到第:9張票
買到第:8張票
買到第:7張票
買到第:6張票
買到第:5張票
買到第:4張票
買到第:3張票
買到第:2張票
買到第:1張票
现在了解了Java中的线程,现在我们就进一步了解更难的知识
线程池
同样的,我们先了解一下什么是线程池
线程池中文文档
线程池英文文档
为什么要使用线程池:
1、开发者可以重复使用线程池中的线程,避免不必要的创建与销毁;
2、可以对线程进行一些简单的操作,例如时间的控制等;
3、控制线程的数量,避免大量线程并发时造成内存的损耗
一个线程执行任务所需要的时间为:创建时间+执行任务时间+销毁时间
若滥用线程,则容易导致不必要的浪费。线程池的使用正是减少了创建和销毁线程的时间,从而达到节省开销的目的。
现在,我们就来了解下线程池
线程池的构造方法:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue )
Executors
工厂方法之一比使用此通用构造方法方便得多。
corePoolSize
- 池中所保存的线程数,包括空闲线程。
maximumPoolSize
- 池中允许的最大线程数。
keepAliveTime
- 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
unit
- keepAliveTime 参数的时间单位。
workQueue
- 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。
IllegalArgumentException
- 如果 corePoolSize 或 keepAliveTime 小于 0,或者 maximumPoolSize 小于等于 0,或者 corePoolSize 大于 maximumPoolSize。
NullPointerException
- 如果 workQueue 为 null
corePoolSize
- 池中所保存的线程数,包括空闲线程。
maximumPoolSize
- 池中允许的最大线程数。
keepAliveTime
- 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
unit
- keepAliveTime 参数的时间单位。
workQueue
- 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。
threadFactory
- 执行程序创建新线程时使用的工厂。
IllegalArgumentException
- 如果 corePoolSize 或 keepAliveTime 小于 0,或者 maximumPoolSize 小于等于 0,或者 corePoolSize 大于 maximumPoolSize。
NullPointerException
- 如果 workQueue 或 threadFactory 为 null。
corePoolSize
- 池中所保存的线程数,包括空闲线程。
maximumPoolSize
- 池中允许的最大线程数。
keepAliveTime
- 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
unit
- keepAliveTime 参数的时间单位。
workQueue
- 执行前用于保持任务的队列。此队列仅由保持 execute 方法提交的 Runnable 任务。
handler
- 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
IllegalArgumentException
- 如果 corePoolSize 或 keepAliveTime 小于 0,或者 maximumPoolSize 小于等于 0,或者 corePoolSize 大于 maximumPoolSize。
NullPointerException
- 如果 workQueue 或 handler 为 null。
corePoolSize
- 池中所保存的线程数,包括空闲线程。
maximumPoolSize
- 池中允许的最大线程数。
keepAliveTime
- 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
unit
- keepAliveTime 参数的时间单位。
workQueue
- 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。
threadFactory
- 执行程序创建新线程时使用的工厂。
handler
- 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
IllegalArgumentException
- 如果 corePoolSize 或 keepAliveTime 小于 0,或者 maximumPoolSize 小于等于 0,或者 corePoolSize 大于 maximumPoolSize。
NullPointerException
- 如果 workQueue、 threadFactory 或 handler 为 null。
这是程池对拒绝任务的处理策略。一般是队列已满或者无法成功执行任务,这时ThreadPoolExecutor会调用handler的rejectedExecution方法来通知调用者
默认有四个拒绝策略
1、ThreadPoolExecutor.AbortPolicy() 直接抛出异常RejectedExecutionException 2、ThreadPoolExecutor.CallerRunsPolicy() 直接调用run方法并且阻塞执行 3、ThreadPoolExecutor.DiscardPolicy() 直接丢弃后来的任务 4、ThreadPoolExecutor.DiscardOldestPolicy() 丢弃在队列中队首的任务
官方定义的四种线程池