Java线程池总结
Java多线程知识中,线程池算是一个比较高级的知识了。但是很多面试或应用中都会有涉及到。
本文将对java线程池的知识做一下简单介绍,并用实际例子做阐述。
一.线程池的作用:
线程池可以很好地提高性能,尤其是当程序中需要创建大量生存周期很短的线程时,更应该考虑使用线程池。
使用线程池可以有效地控制系统中并发线程的数量,当系统中包含大量并发线程时,会导致系统性能剧烈下降,
甚至导致JVM崩溃,而线程池的最大线程数参数可以控制系统中并发线程数不超过此数。
二.线程池的创建
(一)静态方法
从JAVA5开始新增了一个Executors工具类来产生线程池,它有如下几个静态工厂方法来创建线程池。
强烈建议使用较为方便的 Executors 工厂方法
1.Executors.newCachedThreadPool() 创建一个具有缓冲功能的线程池(线程池为无限大),
但是在以前构造的线程可用时将重用它们。
2.Executors.newFixedThreadPool(int) 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程
3.Executors.newSingleThreadExecutor() 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程
4.Executors.newScheduledThreadPool(int corePoolSize) 创建一个线程池,
它可安排在给定延迟后运行命令或者定期地执行
5.Executors.newSingleThreadScheduledExecutor(int corePoolSize) 创建一个只有一条线程的线程池池,
它可安排在给定延迟后运行命令或者定期地执行
上面方法中前三个方法返回的是:ExecutorService对象,
后面两个返回的是ScheduledExecutorService对象是ExecutorService的子类。
ExecutorService代表尽快执行线程的线程池(只要线程池中有空闲的线程,就立即执行线程任务),
程序只要将一个Runnable对象或Callable对象(代表线程任务)提交给该线程,该线程就会尽快执行该任务
ExecutorService的方法:
shutdown() 启动一次顺序关闭线程池,执行以前提交的任务,但不接受新任务。
shutdownNow() 试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表
(二)使用线程池来执行线程任务的步骤
1.调用Executor类的静态方法来创建一个ExecutorService对象,该对象代表一个线程池。
2.调用Runnable实现类的示例,作为线程执行任务。
3.调用ExecutorService对象的submit方法来提交Runnable示例。
4.当不想提交任务时,调用ExecutorService对象的shutdown方法来关闭线程。
(三)线程池的一个简单例子
package com.java.ThreadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PoolTest1 implements Runnable {
public static void main(String[] args) {
// 1.调用Executors类的静态方法来创建一个ExecutorService对象,
ExecutorService pool = Executors.newFixedThreadPool(6);// 创建有最大运行6个线程的线程池。
// 2.创建Runnable实现类的示例,作为线程执行任务。
PoolTest1 p1 = new PoolTest1();
PoolTest1 p2 = new PoolTest1();
// 3.调用ExecutorService对象的submit方法来提交Runnable示例。
pool.submit(p1);// 这是已经开始任务了
pool.submit(p2);
// 4.当不想提交任务时,调用ExecutorService对象的shutdown方法来关闭线程。
pool.shutdown();
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果
这和我们普通线程的使用其实差不多,但是相关类和方法有很大的区别。
其中记住上面的四个步骤是很重要的。
四.下面说一下线程池的相关概述:
(一)什么原因使我们不得不使用线程池?
主要原因是:短时间内需要处理的任务数量很多
(二)使用线程池的好处:
1.减少在创建和销毁线程上所花的时间以及系统资源的开销
2.如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存
(三)FixedThreadPool是一个典型且优秀的线程池,
它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。
但在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。
(四)CachedThreadPool的特点就是在线程池空闲时,即线程池中没有可运行任务时,
它会释放工作线程,从而释放工作线程所占用的资源。但是,但当出现新任务时,
又要创建一新的工作线程,又要一定的系统开销。并且,在使用CachedThreadPool时,
一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。
五.线程池的一个实际应用例题
题目要求:1 创建线程数量为5的线程池。 2 同时运行5个卖票窗口。 3 总票数为100,每隔一秒钟卖一张票
参考答案如下:
package com.java.test5;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 多线程 : 1 创建线程数量为5的线程池。 2 同时运行5个卖票窗口。 3 总票数为100,每隔一秒钟卖一张票
*
* 程序分析:
* 1.线程池类的使用,限制线程的个数
* 2.静态变量的定义
* 3.代码块锁的使用
* */
public class ThreadTest {
public static void main(String[] args) {
// 1.调用Executors类的静态方法来创建一个ExecutorService对象,该对象代表一个线程池。
ExecutorService pool=Executors.newFixedThreadPool(6);//创建5或5以上都可以
// 2.调用Runnable实现类的示例,作为线程执行任务。
//实例化站台对象,并为每一个站台取名字
Station station1=new Station("窗口1");
Station station2=new Station("窗口2");
Station station3=new Station("窗口3");
Station station4=new Station("窗口4");
Station station5=new Station("窗口5");
// 3.调用ExecutorService对象的submit方法来提交Runnable示例。
// 让每一个站台对象各自开始工作
pool.submit(station1);
pool.submit(station2);
pool.submit(station3);
pool.submit(station4);
pool.submit(station5);
// 4.当不想提交任务时,调用ExecutorService对象的shutdown方法来关闭线程。
pool.shutdown();
/*
*如果没有线程池,直接启动线程也能实现效果,但是效率低一点
*station1.start();
station2.start();
station3.start();
station4.start();
station5.start();
*/
}
static class Station extends Thread {
// 通过构造方法给线程名字赋值
public Station(String name) {
super(name);// 给线程名字赋值
}
// 为了保持票数的一致,票数要静态
static int tick = 100;
// 创建一个静态钥匙
static Object ob = " aa";
// 重写run方法
@Override
public void run() {
while (tick > 0) {
synchronized (ob) {// 这个很重要,必须使用一个锁,
// 进去的人会把钥匙拿在手上,出来后才把钥匙拿出来
if (tick > 0) {
System.out.println(getName() + "卖出了第" + tick + "张票");
tick--;
} else {
System.out.println("票卖完了");
}
}
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
本文对java线程池作了简要的描述和使用。其中线程池还有很多相关类和方法没有说到,
想了解的可以自己多去研究。