一、使用线程池的原因
不使用线程池出现的问题:
如果不使用线程池,那么每次需要用的时候就需要创建一个线程,等待线程执行完之后,线程销毁,乍一看没什么问题,但是当并发量足够高的时候,系统就需要花费大量的时候来创建和销毁线程,降低了系统的效率;
什么是线程池:
线程池就是一个用来存放线程的容器
线程池原理:
程序启动的时候创建一定数量的线程放到线程池中,当有需要的时候,就从容器中拿出线程来执行任务,任务执行结束,在放回容器中,复用线程,这样就避免频繁的创建和销毁线程;
线程池的好处:
1.减少资源的消耗
2.提高程序的响应速度
3.提高线程的可管理性
二、线程池-Runnable任务
如何创建线程池:
通过工具类Executors的静态方法
//传入的参数是你要创建的线程数
Executors.newFixedThreadPool(2);
Executors常用方法:
Future submit(Runnable r)
提交Runnable任务
提交Runnable步骤:
1.创建线程池,并指定数量
2.创建类实现runnable接口,重写run方法
3.调用submit方法提交实现runnable的类对象
代码样例:
public class PoleTest {
public static void main(String[] args) {
//创建线程池,指定线程数量
ExecutorService pool = Executors.newFixedThreadPool(2);
//创建实现runnable接口的对象
RunTh th = new RunTh();
//提交任务给线程池
pool.submit(th);
pool.submit(th);
}
}
public class RunTh implements Runnable {
public void run() {
System.out.println("正在运行"+"\t" + Thread.currentThread().getName());
}
}
运行结果:
三、线程池-Callable任务
什么是callable接口:
接口方法:
public V call() throws Exception;
提交Callable任务步骤:
1.创建线程池对象,并指定线程数
2.创建类实现callable方法,并重写call方法(和Runnable中的run方法一样,都是存放线程代码)
3.3.调用submit方法提交实现callable的类对象
代码样例:
public class PoleTest {
public static void main(String[] args) {
//创建线程池,指定线程数量
ExecutorService pool = Executors.newFixedThreadPool(2);
//创建实现callable接口的对象
CallTh callTh = new CallTh();
//提交任务给线程池
pool.submit(callTh);
pool.submit(callTh);
}
}
//由于是泛型类所以随便指定一种类型
public class CallTh implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("正在运行" + "\t" + Thread.currentThread().getName());
return "abc";
}
}
运行结果:
Callable接口的好处:
1.任务执行结束有返回值
2.方法上可以声明异常
所以,如果需要返回值或需要声明异常,就选Callable接口,否则两个都可以选择;
Callable获取返回值:
Future<T> submit (Callable task) ;
//提交callable任务并返回一个Future对象
T get();
//获得callable任务的返回值
线程池常用方法 | 作用 |
---|---|
Future submit (Callable task) | 提交Callable任务 |
Future submit (Runnable task) ; | 提交Runnable任务 |
void shutdown(); | 等待线程池中的任务结束销毁线程池 |
void shutdownNow(); | 立即销毁线程池,不会等待线程池中执行的任务 |
线程池实现多数求和:
public class PoleTest {
public static void main(String[] args) throws Exception {
//创建线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//循环获得前30个数的和
for (int i = 1; i <= 30; i++) {
SumNum num = new SumNum(i);
Future<Integer> submit = pool.submit(num);
System.out.println("前" + i + "个数的和是:" + submit.get());
}
//销毁线程池
pool.shutdown();
}
}
public class SumNum implements Callable<Integer> {
public int n ;
//接收要求前几个数的和
public SumNum(int n) {
this.n = n;
}
@Override
public Integer call() throws Exception {
System.out.println("这是"+Thread.currentThread().getName()
+"线程,在执行前"+n+"个数求和计算!");
int sum = 0;
for (int i = 0; i <= n; i++) {
sum += i;
}
return sum;
}
}
四、线程死锁
什么是死锁:
多个线程在执行过程中,因为抢占资源而出现的相互等待现象称为死锁;
死锁的条件:
1.多个线程
2.多个锁对象
3.同步代码块的嵌套
如何避免死锁:
1.使用单线程
2.使用一把锁
3.不用同步代码块嵌套
线程更多相关内容,请移步上篇文章:JAVA学习笔记之线程