【JavaEE】自主实现java线程池的核心部分

首先思考,线程池最基本的目的是什么?

线程存在“创建”、“运行”、“结束(销毁)”的三个过程,形成所谓的“线程生命周期”。
在CS模式的服务器端,服务器在侦听客户端连接时,每侦听到一个客户端连接请求,都将产生一个线程,这个线程负责维护与客户端的持续通信。若存在大量客户端连接服务器的情况,那么,就会存在大量线程的产生。这种情况,由于每一个线程通常存在比较长的时间,因此,情况不是非常严重。
再看RMI框架:在RMI服务器端,也存在每侦听到一个客户端,就需要产生一个线程,完成客户端对特定方法的调用。这个过程本质上只是执行了一个方法,所以,这个线程将很快结束。那么,在大量客户端都存在向服务器端进行RMI请求的情况下,服务器端会存在海量的线程的“创建”、“销毁”动作。
事实上,线程的“创建”是耗时、耗资源的!如果非常频繁地创建、销毁线程,将使得服务器效率降低!
为避免上述情况,考虑一种可能性:
先创建一些线程,当线程运行结束后,并不直接销毁,而是将它们存放到一个“线程池”中;若有新的线程需求,直接从线程池中获取它们,从而避免频繁、耗资源的线程创建和销毁工作。

上述讨论意味着,每创建一个线程,这个线程都一直保持“运行”状态,直到整个线程池被用户终结,才会结束并销毁。控制同时“运行”的线程数量是必须的。

这里存在一个难点:
如何让一个始终保持运行状态的线程,能切换不同的工作内容?
我们可以设置一个接口,实现接口的类不同,那么,工作内容就不同。代码如下:

public interface ITasker {
	void beforeTask();
	void task();
	void afterTask();
}

这个接口定义了三个方法,实现它的类需要一一实现这些方法。
接下来实现我们执行这些方法的类:

public class Woker implements Runnable{
	private volatile ITasker tasker;
	private volatile boolean goon;

	public Woker() {
		lock = new Object();
		this.goon = false;
	}
	
	public Woker(ITasker tasker) {
		lock = new Object();
		this.tasker = tasker;
		this.goon = false;
	}
	
	public void setTasker(ITasker tasker) {
		this.tasker = tasker;
	}

	public void startup() {
		if (goon == true) {
			return ;
		}
		goon = true;
		new Thread(this).start();
	}
	
	public void shutdown() {
		if (goon == false) {
			return ;
		}
		goon = false;
	}
	
	@Override
	public void run() {
		while (goon) {
			if (tasker != null) {
				try {
					tasker.beforeTask();
					tasker.task();
				} catch (Exception e) {
					e.printStackTrace();
				}
				tasker.afterTask();
				tasker = null;
			}
		}
	}
}

可以看到,这个类实现了run方法,这个线程一旦开启,就是一个死循环,需要shutdown方法来关闭它。
因为,线程池需要通吃管理很多的线程,那么就会有很多的Woker类。这时,我们需要一个总的容器来管理这些Woker类。
但是,我们还需要考虑如下问
题:
1、应该限定“核心线程”数量; 所谓“核心线程”是一直处在待执行状态的线程;
2、当核心线程数量达到极值,后面产生的线程不再进入空线程池;
3、还要控制最大并行线程数量; 若最大并行线程数量达到极值,则,对于用户产生的新的线程需 求,不再立刻满足,而是等待若干时间,再从空线程池中获取可 用线程。
4、对于线程池的结束,应该考虑到正在执行中的线程的“强制结
束”,或者,“等待结束”这两种处理方式。

public class ThreadPool {
	private final Queue<Worker> freeThreadList;
	private final List<Worker> busyThreadList;
	
	public ThreadPool() {
		//	这里用的是队列,因为队列的特性是先入先出,而且是线程安全的
		freeThreadList = new ConcurrentLinkedQueue<Worker>();
		//	这里用的是链表,因为我们需要不断的增加或者删除某个节点。
		busyThreadList = new LinkedList<>();
		//	预先建立5个线程
		createWorker(5);
	}
	
	public boolean stop() {
		Worker worker;
		
		if (busyThreadList.isEmpty()) {
			while ((worker = freeThreadList.poll()) != null) {
				worker.stop();
			}
			return true;
		}
		
		return false;
	}
	
	private void createWorker(int count) {
		for (int i = 0; i < count; i++) {
			Worker worker = new Worker(this);
			worker.start();
			freeThreadList.add(worker);
		}
	}
	
	public void setTask(Tasker tasker) {
		Worker worker = freeThreadList.poll();
		if (worker == null) {
			createWorker(3);
			worker = freeThreadList.poll();
		}
		
		worker.setTasker(tasker);
		busyThreadList.add(worker);
	}
	
	void workerFree(Worker worker) {
		busyThreadList.remove(worker);
		//	这里的20是自己设定的核心线程池数量,也可以作为一个成员来在外面初始化
		if (freeThreadList.size() < 20) {
			freeThreadList.offer(worker);
		} else {
			worker.stop();
		}
	}
	
}

有了ThreadPool类,对于执行完一个Woker,要把它放到freeThreadList里面。我们需要对Woker类进行修改,其实很简单,我们可以给Woker类一个成员,然后在ThreadPool类当我们new Woker时,将自己设置进去。在执行完Woker类里面ITasker接口的方法时,我们需要在Woker类里面调用threadPool的workerFree()方法,同时传递自己作为参数。

到这里,我们也就实现了线程池的核心代码。

你可能感兴趣的:(线程池,java,线程池核心)