虽然创建销毁线程比创建进程更加轻量,但是在频繁创建销毁的时候还是会比较低效
线程池就是为了解决这个问题,如果其中某个线程不再使用了,并不是正真把线程释放,而是放到"另一个池子"中,下次如果需要用到线程就直接从池子中取,不必通过系统来创建了
ExecutorsService和Executors
代码示例:
ExecutorsService表示一个线程池实例
Executors是一个工厂类,能够创建出几种不同风格的线程池
ExecutorsService的submit方法能够向线程池中提交若干个任务
ExecurorService Pool = Executors.newFixedThreadPool(10);
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});
Executors 创建线程池的几种方式
newFixedThreadPool:创建固定线程数的线程池
newCachedThreadPool:创建线程数目动态增长的线程池
newSingleThreadExecutor:创建只包含单个线程的线程池
newScheduledThreadPool:设定 延迟时间后执行命令,或者定期执行命令,是进阶版的Timer
Executors本质上是ThreadPoolExecutor类的封装
ThreadPoolExecutor
ThreadPoolExecutor提供了更多的可选参数,可以进一步细化参数线程池行为的设定
ThreadPoolExecutor的构造方法
理解ThreadPoolExecutor构造方法的参数
把创建一个线程池想象成开个公司,每个员工相当于一个线程
keepAliveTime:临时工允许的空闲时间
unit:keepaliveTime的时间的单位,是秒,分钟,还是其他值
代码示例:
ExecutorService pool = new ThreadpoolExecutor(1,2,1000,TimeUnit.MILLISECONDS,
new SynchronousQueue<Runnable<(),
Executors.defaultThreadFactory(),
new
ThreadPoolExecutor.AbortPolicy());
for(int i = 0;i<3;i++) {
pool.submit(new Runnable() {
@Override
void run() {
System.out.println("hello");
}
});
}
线程池的工作流程
信号量Semaphore
信号量,用来表示"可用资源的个数"本质上就是一个计时器
理解信号量:
可以把信号量想象成停车场的展示牌,当前有一百个车位,表示有100个可用资源,当有车开进去的时候,就相当于申请一个可用资源,可用车位就-1(这个称为信号量的p操作)
当有车开出来的时候,就相当于释放一个可用资源,可用车位就+1(这个称为信号量的v操作)
如果计数器的值已经为0了,还尝试申请资源,就会阻塞等待,知道有其他线程释放资源
Semaphore的pv操作中的加减计数器操作都是原子的,可以在多线程下直接使用
代码示例:
创建Semaphore示例,初始化为4个可用资源
acquire方法表示申请资源(p操作),release方法表示释放资源(v操作)
创建20个线程,每个线程都尝试申请资源,sleep1秒之后,释放资源,观察程序的执行效果
Semaphroe semphroe = new Semaphroe(4);
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
System.out.println("申请资源");
semaphore.acquire();
System.out.println("我获取到了资源");
Thread.sleep(1000);
System.out.println("我释放了资源");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
for(int i = 0;i<20;i++) {
Thread t = new Thread(runnable);
t.start();
}
CountDownLatch
同时等待N个任务执行结束:
好像跑步比赛,10个选手依次就位,哨声响才同时出发,所有选手都通过终点,才能公布成绩
构造CountDownLatch实例,初始化10表示有10个任务需要完成
每个任务执行完毕,都调用latch.countDown(),在CountDown Latch内部的计数器同时自减
主线程使用latch.await();阻塞等待所有执行完毕,相当于计数器为0了
public class Demo {
public static void main(String[] args) throws Exception{
CountDowlLatch latch = new CountDownLatch(10);
Runnable r = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(Match.random() * 10000);
latch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
};
for (int i = 0;i<10;i++) {
new Thread(r).start();
}
//必须等到10人全部回来
latch.await();
System.out.println("比赛结束");
}
}