并发是伴随着多核处理器的诞生而“席卷大地”的,为了充分利用硬件资源,诞生了多线程技术。但是多线程又存在资源竞争的问题,引发了同步和互斥的问题,JDK1.5推出的java.util.concurrent(简称JUC)并发工具包,就是专门来解决这些问题的。
new Thread的弊端:
◎new Thread新建对象,性能差。
◎线程缺乏统一的管理,可能无限制的新建线程,相互竞争,严重时会占用过多系统资源或OOM(内存溢出)。
ThreadPool线程池:
◎重用存在的线程,减少对象新建、消亡的开销。
◎线程总数可控,提高资源的利用率。
◎避免过多资源竞争,避免阻塞。
◎提供额外功能,定时执行、定期执行,监控等。
JUC提供了调度器对象Executors来创建线程池,可创建的线程池有四种
①CachedThreadPool可缓存线程池
特点: 1.如果线程池中无空闲线程,则创建;有,则利用起来。
2.线程总数固定。
3.可以进行自动线程回收。
②FixedThreadPool定长线程池
特点: 1.如果线程池中有空闲线程,则利用起来;如果线程池中的所有线程都在执行任务,那么后续任务进入等待状态;
直到有空闲的线程了,该线程再执行后续任务。
2.无线程数上限。
注:如果有多个后续任务的话,那么空闲下来的线程会根据FIFO原则,来选择要执行哪一个线程。
(FIFO:即First Input First Output,先入先出;还有LIFI:即Last In First Out后入先出)
③SingleThreadExecutor单线程线程池
说明: 用来模拟单线程的线程池
④ScheduledThreadPool调度线程池
说明: 一般用于处理定时任务;与其类似的还有Timer;在实际做项目时,我们既不用ScheduledThreadPool,
也不用Timer;而是用成熟的定时任务框架Quartz或Spring自带的定时调度。
注:Quartz或Spring定时调度的具体用法,可详见我的这篇博客
https://blog.csdn.net/justry_deng/article/details/80666508。
四种线程池的创建使用示例
CachedThreadPool可缓存线程池,使用示例:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* CachedThreadPool的创建
*
* @author JustryDeng
* @date 2018/10/11 18:47
*/
public class ExecutorsCreateCachedThreadPool {
private static Integer count = 10000;
private static Object object = new Object();
public static void main(String[] args) throws InterruptedException {
// -> 创建可缓存线程池
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 1; i <= 10000; i++) {
// 使用lambda表达式简单实现Runnable接口的run方法
executorService.execute(() -> {
// 只有获得了object的锁的线程,才能操作
synchronized (object) {
count--;
}
});
}
// 当线程池中所有线程都运行完毕后,关闭线程池
executorService.shutdown();
// 主线程阻塞2秒再输出count的值,为了避免输出打印count的值时,其余线程还没计算完;导致输出的不是count的最终值
Thread.sleep(2000);
System.out.println(count);
}
}
FixedThreadPool定长线程池,使用示例:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* FixedThreadPool的创建
*
* @author JustryDeng
* @date 2018/10/11 18:47
*/
public class ExecutorsCreateFixedThreadPool {
private static Integer count = 10000;
private static Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
// -> 创建定长线程池(该线程池中总共存在200个线程)
ExecutorService executorService = Executors.newFixedThreadPool(200);
for (int i = 1; i <= 10000; i++) {
// 使用lambda表达式简单实现Runnable接口的run方法
executorService.execute(() -> {
// 使用重入锁,保证线程安全同步
lock.lock();
try {
count--;
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
}
// 当线程池中所有线程(包括排着队的)都运行完毕后,关闭线程池
executorService.shutdown();
// 主线程阻塞2秒再输出count的值,为了避免输出打印count的值时,其余线程还没计算完;导致输出的不是count的最终值
Thread.sleep(2000);
System.out.println(count);
}
}
SingleThreadExecutor单线程线程池,使用示例:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* SingleThreadExecutor的创建
*
* @author JustryDeng
* @date 2018/10/11 18:47
*/
public class ExecutorsCreateSingleThreadExecutor {
private static Integer count = 10000;
public static void main(String[] args) throws InterruptedException {
// -> 创建定单线程线程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 1; i <= 10000; i++) {
// 使用lambda表达式简单实现Runnable接口的run方法
// 因为是单线程池,所以不需要任何其他操作,就能保证数据的安全准确性
executorService.execute(() -> count--);
}
// 当线程池中所有线程(包括排着队的)都运行完毕后,关闭线程池
executorService.shutdown();
// 主线程阻塞2秒再输出count的值,为了避免输出打印count的值时,其余线程还没计算完;导致输出的不是count的最终值
Thread.sleep(2000);
System.out.println(count);
}
}
ScheduledThreadPool调度线程池,使用示例:
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* ScheduledThreadPool的创建
*
* @author JustryDeng
* @date 2018/10/11 18:47
*/
public class ExecutorsCreateScheduledThreadPool {
public static void main(String[] args) {
// -> 创建定时调度线程池(初始化核心线程数为5)
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
// 使用lambda表达式简单实现Runnable接口的run方法
System.out.println("当前时间是:" + new Date());
scheduledExecutorService.schedule(() -> System.out.println("10秒后输出此语句! -> " + new Date()),10, TimeUnit.SECONDS);
// 当线程池中所有线程(包括排着队的)都运行完毕后,关闭线程池
scheduledExecutorService.shutdown();
}
}