线程池
提前创建好若干个线程放在一个容器中。如果有任务需要处理,则将任务 直接分配给线程池中的线程来执行,任务处理完以后这个线程不会被销毁, 而是等待后续分配任务
一下4个线程,其实都是ThreadPoolExecutor的实现类。
newFixedThreadPool
- 创建一个固定数量的线程池
newSingleThreadExecutor
- 创建只有一个线程的线程池
newCachedThreadPool
- 创建一个可以根据实际情况调整的线程池
newScheduledThreadPool
- 创一个带调度定时任务的线程池(延迟执行、周期性执行的线程池:类似于定时器的功能)
public class ThreadPoolDemo implements Runnable{
public static void main(String[] args) {
ExecutorService executorService=Executors.newFixedThreadPool(3);
executorService.prestartAllCoreThreads(); //可以提前预热所有核心线程
for (int i = 0; i < 100; i++) {
executorService.execute(new ThreadPoolDemo());
}
executorService.shutdown();
}
@Override
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
线程池的6大参数
核心线程数,相当于正式工
最大线程数,相当于外包可增加可减少
keepAliveTime:超时时间, 超出核心线程以外的线程的生存周期。
unit:存活的时间单位
workQueue:保存等待执行任务的队列
threadFactory:创建新线程使用的工厂
线程池原理图:
一、线程池的监控
继承线程池,之前说的4中线程的实现方法本身就是ThreadPoolExcutor的4中实现
public class ThreadPoolSelf extends ThreadPoolExecutor {
public ThreadPoolSelf(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
public void shutdown() {
super.shutdown();
}
//任务执行开始
@Override
protected void beforeExecute(Thread t, Runnable r) {
//TODO 通过一个属性来记录任务的开始时间
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
//任务执行结束
System.out.println("初始线程数:"+this.getPoolSize());
System.out.println("核心线程数:"+this.getCorePoolSize());
System.out.println("正在执行的任务数量:"+this.getActiveCount());
System.out.println("已经执行的任务数:"+this.getCompletedTaskCount());
System.out.println("任务总数"+this.getTaskCount());
}
}
二、带返回值的线程及原理
带返回值的线程使用submit方法
public class CallableFutureDemo implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("Hello Mic");
Thread.sleep(3000); //睡眠3s
return "Mic";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
/* CallableFutureDemo callableFutureDemo=new CallableFutureDemo();
FutureTask futureTask=new FutureTask(callableFutureDemo);
new Thread(futureTask).start();
//get方法是属于阻塞方法
System.out.println(futureTask.get());*/
ExecutorService executorService = Executors.newFixedThreadPool(1);
CallableFutureDemo callableFutureDemo = new CallableFutureDemo();
Future future = executorService.submit(callableFutureDemo);
System.out.println(future.get());
}
}
三、一不小心死锁了怎么办
3.1 死锁的4个条件,需要同时满足
互斥,共享资源 X 和 Y 只能被一个线程占用;
占有且等待,线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候, 不释放共享资源 X;
不可抢占,其他线程不能强行抢占线程 T1 占有的资源;
循环等待,线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占 有的资源,就是循环等待。
解决方案:
- 一次性申请所有资源,这样就不存在等待了
- 占用一部分资源的线程可以继续申请资源,申请不到需要主动释放其他占有的资源
- 按照申请的顺序进行资源的预占用,规避循环等待
转账案例:
会出现死锁
public class Account {
private String accountName;
private int balance; //余额
public Account(String accountName, int balance) {
this.accountName = accountName;
this.balance = balance;
}
public void debit(int amount){
this.balance-=amount;
}
public void credbit(int amount){
this.balance+=amount;
}
}
public class TransferAccount implements Runnable{
private Account fromAccount; //转出账户
private Account toAccount; //转入账户
private int amount;// 转出金额
public TransferAccount(Account fromAccount, Account toAccount, int amount) {
this.fromAccount = fromAccount;
this.toAccount = toAccount;
this.amount = amount;
}
@Override
public void run() {
while(true){
synchronized (fromAccount){
synchronized (toAccount){
if(fromAccount.getBalance()>=amount){
fromAccount.debit(amount);
toAccount.credbit(amount);
}
}
System.out.println(fromAccount.getAccountName()+"----"+fromAccount.getBalance());
System.out.println(toAccount.getAccountName()+"----"+toAccount.getBalance());
}
}
}
}
3.2 方案一
一次性申请所有资源 新建一个锁对象。
public class Allocator {
private List<Object> list = new ArrayList<>();
/**
* 申请资源
* @param from
* @param to
* @return
*/
synchronized boolean apply(Object from,Object to){
// list不为空,说明别的线程有占用
if(list.contains(from) || list.contains(to)){
return false;
}else {
// 可申请,并把锁加入
list.add(from);
list.add(to);
return true;
}
}
synchronized void free(Object from,Object to){
list.remove(from);
list.remove(to);
}
}
public class TransferAccount01 implements Runnable{
private Account fromAccount; //转出账户
private Account toAccount; //转入账户
private int amount;// 转出金额
Allocator allocator;// 新的锁对象
public TransferAccount01(Account fromAccount, Account toAccount, int amount,Allocator allocator) {
this.fromAccount = fromAccount;
this.toAccount = toAccount;
this.amount = amount;
this.allocator=allocator;
}
@Override
public void run() {
while(true){
if(allocator.apply(fromAccount,toAccount)) { //都会在这个地方去获得临界资源
try {
synchronized (fromAccount) {
synchronized (toAccount) {//申请不到资源,就已经阻塞
if (fromAccount.getBalance() >= amount) {
fromAccount.debit(amount);
toAccount.credbit(amount);
}
}
System.out.println(fromAccount.getAccountName() + "----" + fromAccount.getBalance());
System.out.println(toAccount.getAccountName() + "----" + toAccount.getBalance());
}
}finally {
allocator.free(fromAccount,toAccount);
}
}
}
}
}
3.3 方案二
占用一部分资源的线程可以继续申请资源,申请不到需要主动释放其他占有的资源
public class TransferAccount02 implements Runnable {
private Account fromAccount; //转出账户
private Account toAccount; //转入账户
private int amount;// 准出金额
Lock fromLock = new ReentrantLock();
Lock toLock = new ReentrantLock();
public TransferAccount02(Account fromAccount, Account toAccount, int amount) {
this.fromAccount = fromAccount;
this.toAccount = toAccount;
this.amount = amount;
}
@Override
public void run() {
while (true) {
if (fromLock.tryLock()) {
if (toLock.tryLock()) {
if (fromAccount.getBalance() >= amount) {
fromAccount.debit(amount);
toAccount.credbit(amount);
}
System.out.println(fromAccount.getAccountName() + "----" + fromAccount.getBalance());
System.out.println(toAccount.getAccountName() + "----" + toAccount.getBalance());
}
}
}
}
}
3.4 方案三
按照申请的顺序进行资源的预占用,规避循环等待
三、ConcurrentHashMap
J.U.C包中提供的线程安全比那个且高效的HashMap
ount.getAccountName() + “----” + fromAccount.getBalance());
System.out.println(toAccount.getAccountName() + “----” + toAccount.getBalance());
}
}
}
}
}
### 3.4 方案三
按照申请的顺序进行资源的预占用,规避循环等待
## 三、ConcurrentHashMap
> J.U.C包中提供的线程安全比那个且高效的HashMap
![在这里插入图片描述](https://img-blog.csdnimg.cn/8a860a1b63b54892a68432f21a8a2026.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAdTAxMDg2ODE5Mg==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)