一、线程池概念
在多线程的程序里面,经常要创建线程,但是创建线程的过程是十分耗费资源的,而且线程是一种可以重复利用的资源,如果一个线程可以重复反复利用,处理完一个任务后用来处理另外一个任务,那么可以节省很多的资源。因此产生了线程池,一个用来统一管理线程一遍提高程序的效率的功能。
二、线程池的原理
线程池里面含有两个存储元素的数据结构,一个是存储任务的阻塞队列(也可以用简单的队列来代替),另外一个是用来存储线程的数组,当队列里面有任务时,任务队列里面的任务会被放到线程里面执行,线程解决完一个任务在取出另外的任务,并处理,当任务队列里面没有任务时所有线程进入等待状态。达到了线程重复利用的效果。

三、封装任务线程
在存储任务线程的数组里面,我们存储的不是Thread,而是把任务封装成WorkThread,并继承Runnable,在通过这样的封装后我们可以对任务线程进行更加精细的操作,并提供更加多的拓展。该类的重要的方法是在任务的队列里面获取任务,并使任务执行起来,同时还要判断任务线程是否要进入等待状态。
public void run() {
while (true){
//Lock写法
Runnable work = null;
try {
lock.lock();
//判断当前
while (workList.isEmpty()){
System.out.println("任务队列为空"+Thread.currentThread().getName()+"进入等待");
notEmpty.await();
}
work = workList.remove(0);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
work.run();
}
}
代码判断任务队列是否为空,如果是,使线程进入等待状态。特别注意的是代码里面的work里面的run方法不可以放在锁的代码块里面,否则会有严重的线程闲置问题,只有在取出任务队列的元素才要保证线程安全。work的run方法英因为WorkThread已经放入了Thread里面,所以没有必要在新建Thread。直接run就好了。
四、简单线程池的编写
线程池里面的重要方法是添加任务,并在初始化时创建对应的线程数。
public class MyThreadPool {
//管理任务线程
public ArrayList<WorkThread> threadList = new ArrayList<>();
//管理任务
public ArrayList<Runnable> workList = new ArrayList<>();
public ReentrantLock lock = new ReentrantLock();
public Condition notEmpty = lock.newCondition();
//添加任务线程
public MyThreadPool(int core){
for(int i = 0; i < core; i++){
WorkThread workThread = new WorkThread(workList, lock, notEmpty);
new Thread(workThread).start();
threadList.add(workThread);
}
}
/**
* 提交任务方法
* @param runnable 提交的任务
*/
public void addWork(Runnable runnable){
try {
lock.lock();
workList.add(runnable);
notEmpty.signal();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
要注意的是WorkThread和线程池里面的Lock和Condition都要保持一致。
五、idea内置线程池的最大线程数
idea里面的内置线程池是一定的当任务队列被装满时,而且此时的核心线程都被占用,此时的会唤醒额外的线程数 。如果此时还向线程池里面添加任务,那么线程池会拒绝并抛弃任务。线程池里面还有存活时间,当任务队列为空,超过存活时间,那么线程重新进入等待状态。
六、实现最大线程数
我们把上面的存储任务的数组换成自己写的阻塞队列,使用时把线程池里面的Lock换成阻塞队列里面的Lock就好了,在改改参数名和方法名。阻塞队列源码如下:
public class MyQueue2 <T>{
//队列大小
int length;
//当前队列的数据
int size = 0;
int head = 0;
int tail = 0;
T[] queue;
public ReentrantLock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public MyQueue2(int length){
this.length = length;
queue = (T[]) new Object[length];
}
//存入数据
public void put(T t) throws Exception{
lock.lock();
try {
//线程睡眠 阻塞
while (length == size){
//System.out.println("队列已满,进入阻塞状态");
notFull.await();
}
queue[head] = t;
head = (head + 1) % length;
size++;
notEmpty.signal();
} finally {
lock.unlock();
}
}
//取出数据
public T take() throws Exception{
lock.lock();
try {
while (size == 0){
//System.out.println("队列为空,进入阻塞状态");
//使队列进入等待状态
notEmpty.await();
}
T data = queue[tail];
queue[tail] = null;
tail = (tail + 1) % length;
size--;
notFull.signal();
return data;
} finally {
lock.unlock();
}
}
//判断队列是否为空
public boolean isEmpty(){
if(size == 0){
return true;
}
return false;
}
//是否满了
public boolean isFull(){
if(size == length){
return true;
}
return false;
}
}
想要实现上面的最大线程数,我们可以额外创建一个ArrayList存储他们,里面的线程在判断阻塞队列已满,所以核心线程处于工作状态时进入等待状态。其他方法和原本的WorkThread完全一样。我们可以直接继承WorkThread,重写里面的run()方法,添加一个核心线程队列。就可以实现了。
public class ExtraWorkThread extends WorkThread{
public ArrayList<WorkThread> threads;
public ExtraWorkThread(ArrayList<WorkThread> threads, MyQueue2<Runnable> workList, ReentrantLock lock, Condition notEmpty) {
super(workList, lock, notEmpty);
this.threads = threads;
}
@Override
public void run() {
while (true){
//Lock写法
Runnable work = null;
try {
lock.lock();
while (!(isWork() && workList.isFull())){
//System.out.println("任务不繁忙," + Thread.currentThread().getName()+ "进入等待状态");
notEmpty.await();
}
//work = workList.remove(0);
work = workList.take();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
work.run();
}
}
public boolean isWork(){
for(int i = 0; i < threads.size(); i++){
WorkThread thread = threads.get(i);
if(thread.sleep){
return false;
}
}
return true;
}
}
在修改一下WorkThread,设置一个sleep标签,方便判断是否处于工作状态。在线程进入等待状态时设置成false,取出元素使设置为true。修改后的代码如下
public class WorkThread implements Runnable{
MyQueue2<Runnable> workList;
//ArrayList<Runnable> workList;
public ReentrantLock lock;
public Condition notEmpty;
public boolean sleep;
public WorkThread(MyQueue2<Runnable> workList, ReentrantLock lock, Condition notEmpty){
this.workList = workList;
this.lock = lock;
this.notEmpty = notEmpty;
}
@Override
public void run() {
while (true){
//Lock写法
Runnable work = null;
try {
lock.lock();
//判断当前
while (workList.isEmpty()){
System.out.println("任务队列为空"+Thread.currentThread().getName()+"进入等待");
sleep = true;
notEmpty.await();
}
sleep = false;
//work = workList.remove(0);
work = workList.take();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
work.run();
}
}
}
这样就可以了
2247

被折叠的 条评论
为什么被折叠?



