目录
线程池
1.为什么使用线程池(优势)
- 我们使用线程的时候就去创建一个线程,这样实现起来非常简单,
- 但是就会出现一个问题:如果并发的线程数量很多,并且每一个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
- 有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是继续执行其它任务,在java中可以通过线程池来表达这样的效果。
2.线程池底层原理
- 线程池可以理解为容器(集合),一般使用集合(ArrayList,HashSet,LinkedList,HashMap)来充当容器,
- 常用LinkedList<Thread>集合作为容器,泛型就是线程即集合中存放的都是线程。下面是在LinkedList集合中通过add方法向集合中添加一些线程即add(new Thread(xxx))。
- 1.当程序第一次启动时,创建多个线程,保存到一个集合中。
- 2.当我们想要使用线程的时候,就可以从集合中取出线程使用。
- Thread t=list.remove(0):从List集合中移除第0个线程(线程只能被一个任务使用)
- Thread t=linked.removeFirst():从LikedList集合中移除第1个线程
- 3.当我们使用完线程,需要将线程归还给线程池:即将线程再添加到集合中,(这里面有队列机制,从对头取出,队尾归还)
- 若是List集合,则list.add(t);将线程归还到list集合末尾
- 若是LinkedList集合,则linked.addLast(t);将线程归还到LinkedList集合尾端。
注意:
- JDK1.5之后,JDK内置了线程池,我们可以直接使用。
3.几种线程池如何工作的?
任务加入后各个线程池执行流程:
缓存线程池
- 1.判断线程池是否存在空闲线程,存在则使用,不存在则创建线程放入线程池再使用。
- 2.长度无限制。
定长线程池
- 判断线程池是否存在空闲线程,存在则使用
- 不存在空闲线程,且线程池未满,则创建线程放入线程池,再使用。
- 不存在空闲线程,且线程池已满,则等待出现空闲线程再使用
- 线程池线程长度是指定的数值。
单线程线程池
- 判断线程池的那个线程是否空闲,空闲则使用,不空闲则等待这个线程空闲再用。
- 池中只有一个线程,相当于获取定长线程池传参1效果。
周期定长线程池
- 判断线程池是否存在空闲线程,存在则使用。
- 不存在空闲线程,且线程池未满,则创建线程放入线程池,然后使用。
- 不存在空闲线程,且线程池已满,则等待线程池存在空闲线程。
- 实现定时执行或定时周期执行任务,当某个时机触发时,自动执行某个任务。
4.几种线程池的使用Demo
线程池:JDK1.5之后提供的
- 1.java.util.concurrent.Executors:线程池工厂类,用来创建线程池
- 2.Executors类中的静态方法:
static ExecutorService newFixedThreadPool(int nThreads){}
创建一个可重用 固定线程数的线程池
参数:int nThreads:创建线程池中包含的线程数量
返回值:返回的是ExecutorService接口的实现类对象,可以使用该接口接收(面向接口编程)
- 3.java.util.concurrent.ExecutorService:线程池接口
- 用来从线程池中获取线程,调用start方法,执行线程任务
- submit(Runnable task)提交一个Runnable任务用于执行
- 关闭/销毁线程池的方法void shutdowm()
* 4.线程池的使用步骤:
- 1.使用线程池的工厂类Executors里面提供的静态方法newFixedThreadPool创建一个指定线程数量的线程池,并用ExecutorService接口接收。
- 2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
- 3.调用ExecutorService中方法submit,传递线程任务(实现类),开启线程,执行run方法
- 4.调用ExecutorService中的方法shutdown销毁销毁线程池(不建议)
package Thread.demo17.ThreadPool;
/**
* 2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
*/
public class RunnableImpI implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"创建一个新的" +"线程执行");
}
}
package Thread.demo17.ThreadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo1ThreadPool {
public static void main(String[] args) {
//1.使用线程池的工厂类Executors里面提供的静态方法newFixedThreadPool
// 创建一个指定线程数量的线程池,并用ExecutorService接口接收。
ExecutorService es= Executors.newFixedThreadPool(2);//两个线程
//3.线程池调用ExecutorService中submit,传递线程任务(实现类),开启线程,执行run方法
//传递三个线程任务,这三个线程任务相同,也可以创建多个实现类(线程任务)
es.submit(new RunnableImpI());//pool-1-thread-2创建一个新的线程执行
es.submit(new RunnableImpI());//pool-1-thread-1创建一个新的线程执行
//线程池会一直开启,使用完了线程,会自动把线程归还线程池,线程可以继续使用
es.submit(new RunnableImpI());//pool-1-thread-1创建一个新的线程执行
//4.调用ExecutorService中的方法shutdown销毁销毁线程池(不建议)
es.shutdown();
}
}
CachedThreadPool
package demo24threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 缓存线程池的使用
* 1.判断线程池是否存在空闲线程,存在则使用,不存在则创建线程放入线程池再使用。
* 2.长度无限制。
*/
public class CachedThreadPool {
public static void main(String[] args) {
//获取缓存线程池
ExecutorService service = Executors.newCachedThreadPool();
//指挥线程池执行线程任务(线程任务使用匿名内部类写法)
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+"你好啊");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+"我很好");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+"我也很好");
}
});
try {
Thread.sleep(5000);
System.out.println("休眠5s");
} catch (InterruptedException e) {
e.printStackTrace();
}
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+"我还好");
}
});
}
}
FixedThreadPool
package demo24threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 定长线程池:即指定线程池线程数量
* 判断线程池是否存在空闲线程,存在则使用
* 不存在空闲线程,且线程池未满,则创建线程放入线程池,再使用。
* 不存在空闲线程,且线程池已满,则等待出现空闲线程再使用
* 线程池线程长度是指定的数值。
*/
public class FixedThreadPool {
public static void main(String[] args) {
//获取定长线程池,设置池中线程数为2
ExecutorService service = Executors.newFixedThreadPool(2);
//线程池分配线程执行线程任务(任务使用匿名内部类)
service.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println("线程1开始执行任务1,此处休眠3s,线程1一直被占用");
Thread.sleep(3000);
System.out.println("休眠结束,线程1继续执行线程任务");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+"任务1:你好啊");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+"任务2:我很好");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+"任务3:我也很好");
}
});
}
}
SingleThreadPool
package demo24threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 单线程线程池:
* 判断线程池的那个线程是否空闲,空闲则使用,不空闲则等待这个线程空闲再用。
* 池中只有一个线程,相当于获取定长线程池传参1效果
*/
public class SingleThreadPool {
public static void main(String[] args) {
//获取单线程线程池
ExecutorService service = Executors.newSingleThreadExecutor();
//线程池分配线程执行线程任务
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+"你好啊");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+"我很好");
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+"我也很好");
}
});
}
}
ScheduledThreadPool
package demo24threadpool;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 周期定长线程池
*
* 判断线程池是否存在空闲线程,存在则使用。
* 不存在空闲线程,且线程池未满,则创建线程放入线程池,然后使用。
* 不存在空闲线程,且线程池已满,则等待线程池存在空闲线程。
* 实现定时执行或定时周期执行任务,当某个时机触发时,自动执行某个任务。
*/
public class ScheduledThreadPool {
public static void main(String[] args) {
//获取周期定长线程池,设置池中线程数为2
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
//线程池分配线程执行线程任务
//1.定时执行一次,定时3s后执行
//参数1:为要定时执行的任务
//参数2:设置定时时长
//参数3:设置时长单位,使用TimeUnit工具类中常量指定
service.schedule(new Runnable(){
@Override
public void run() {
System.out.println("定时3s后线程执行线程任务");
System.out.println(Thread.currentThread().getName() + ";你好啊");
}
}, 3, TimeUnit.SECONDS);
//2.周期执行任务,5s后每隔1s执行一次任务
//参数1:线程任务
//参数2:延迟时长(即第一次执行在什么时间以后)
//参数3:周期时长(每隔多久执行一次)
//参数4:时长单位,使用TimeUnit工具类
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("5s后每隔1s执行一次线程任务");
System.out.println(Thread.currentThread().getName() + ";你好啊");
}
},5,1,TimeUnit.SECONDS);
}
}
5.使用线程池好处
- 1.降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
- 2.提高响应速度。当任务到达时,任务不需要等到线程创建就能立即执行。
- 3.提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多的内存,而把服务器搞垮(每个线程需要大约1MB,线程开的越多,消耗的内存也就越大)