目录
一:线程的状态
(1)初始状态
实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态。
1)就绪状态只是说你资格运行,调度程序没有挑选到你,你就永远是就绪状态。
2)调用线程的start()方法,此线程进入就绪状态。
3)当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。
4)当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。
5)锁池里的线程拿到对象锁后,进入就绪状态。
线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。
阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。
处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。
处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。
1)当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。
2)在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
二:线程池的构造方式
ThreadPoolExecutor的构造函数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
函数的参数含义如下:
corePoolSize: 线程池核心线程数
maximumPoolSize:线程池最大数
keepAliveTime: 空闲线程存活时间
unit: 时间单位
workQueue: 线程池所使用的缓冲队列
threadFactory:线程池创建线程使用的工厂
handler: 线程池对拒绝任务的处理策略
三:线程池的特性
(1):当池中正在运行的线程数(包括空闲线程)小于corePoolSize时,新建线程执行任务。
下面用实验来说明,代码如下:
public class TestThreadPoolExecutor {
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1));
//任务1
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println("-------------helloworld_001---------------" + Thread.currentThread().getName());
}
});
try {
//主线程睡2秒
Thread.sleep(2*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//任务2
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println("-------------helloworld_002---------------" + Thread.currentThread().getName());
}
});
}
}
实验结果如下:
实验结果分析:
从实验结果上可以看出,当执行任务1的线程(thread-1)执行完成之后,任务2并没有去复用thread-1而是新建线程(thread-2)去执行任务。
(2):当池中正在运行的线程数大于等于corePoolSize时,新插入的任务进入workQueue排队(如果workQueue长度允许),等待空闲线程来执行。
下面用实验来说明,代码如下:
public class TestThreadPoolExecutor {
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1));
// 任务1
pool.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3 * 1000);
System.out.println("-------------helloworld_001---------------" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 任务2
pool.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5 * 1000);
System.out.println("-------------helloworld_002---------------" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 任务3
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println("-------------helloworld_003---------------" + Thread.currentThread().getName());
}
});
}
}
实验结果如下:
实验结果分析:
从实验结果上看,任务3会等待任务1执行完之后,有了空闲线程,才会执行。并没有新建线程执行任务3,这时maximumPoolSize=3这个参数不起作用。
(3):当队列里的任务数达到上限,并且池中正在运行的线程数小于maximumPoolSize,对于新加入的任务,新建线程。
下面用实验来说明,代码如下:
public class TestThreadPoolExecutor {
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1));
// 任务1
pool.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3 * 1000);
System.out.println("-------------helloworld_001---------------" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 任务2
pool.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5 * 1000);
System.out.println("-------------helloworld_002---------------" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 任务3
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println("-------------helloworld_003---------------" + Thread.currentThread().getName());
}
});
// 任务4
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println("-------------helloworld_004---------------" + Thread.currentThread().getName());
}
});
}
}
实验结果如下:
实验结果分析:
当任务4进入队列时发现队列的长度已经到了上限,所以无法进入队列排队,而此时正在运行的线程数(2)小于maximumPoolSize所以新建线程执行该任务。
(4):当队列里的任务数达到上限,并且池中正在运行的线程数等于maximumPoolSize,对于新加入的任务,执行拒绝策略(线程池默认的拒绝策略是抛异常)。
下面用实验来说明,代码如下:
public class TestThreadPoolExecutor {
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1));
// 任务1
pool.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3 * 1000);
System.out.println("-------------helloworld_001---------------" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 任务2
pool.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5 * 1000);
System.out.println("-------------helloworld_002---------------" + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 任务3
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println("-------------helloworld_003---------------" + Thread.currentThread().getName());
}
});
// 任务4
pool.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-------------helloworld_004---------------" + Thread.currentThread().getName());
}
});
// 任务5
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println("-------------helloworld_005---------------" + Thread.currentThread().getName());
}
});
}
}
实验结果如下:
实验结果分析:
当任务5加入时,队列达到上限,池内运行的线程数达到最大,故执行默认的拒绝策略,抛异常。
队列类型虽然仅限于LinkedBlockingQueue这一种队列类型,但总结出来的特性,对与常用ArrayBlockingQueue 和 SynchronousQueue同样适用。