首页 > 编程语言 > 详细

如何模拟实现一个线程池

时间:2020-03-29 16:34:23      阅读:96      评论:0      收藏:0      [点我收藏+]

模拟实现一个线程池

抛出一个问题:

当我们实例化一个线程类(即:Thread类)并提交一个任务(实现了Runnable接口的类)后,只要主动调用该线程类对象的start()方法即可启动一个线程任务,但是任务执行完成后我们却无法再启动该任务或者是向该线程类对象提交任务。但为什么线程池却可以重复接受任务并执行?

思考过程:

1.我们向线程池提交任务都是调用 java.util.concurrent.ThreadPoolExecutor.submit方法,而全部的submit方法最终都是调用execute方法(该方法会进行线程三步骤执行),execute方法最终执行的是 java.util.concurrent.ThreadPoolExecutor.addWorker方法,该方法会判读是将任务提交给“队列”或者是启动新的线程【见:execute方法源码】。如果是启动新的线程的话,则会实例化一个java.util.concurrent.ThreadPoolExecutor.Worker类,该类实现了Runnable接口同时持有一个Thread类对象,此时线程任务就在Worker类的run方法里面【见:Worker重复执行任务的实现】,该类对象里面的任务对象是this即该worker对象,最后调用线程类的start方法启动该线程。

execute方法源码
  public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn‘t, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
		//判断如果核心线程满了则尝试调用offer方法向队列添加任务
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

Worker重复执行任务的实现

Worker的run方法最终执行的是runWorker方法,源码如下:

   /**
     * Main worker run loop.  Repeatedly gets tasks from queue and
     * executes them, while coping with a number of issues:
     *
     * 1. We may start out with an initial task, in which case we
     * don‘t need to get the first one. Otherwise, as long as pool is
     * running, we get tasks from getTask. If it returns null then the
     * worker exits due to changed pool state or configuration
     * parameters.  Other exits result from exception throws in
     * external code, in which case completedAbruptly holds, which
     * usually leads processWorkerExit to replace this thread.
     *
     * 2. Before running any task, the lock is acquired to prevent
     * other pool interrupts while the task is executing, and then we
     * ensure that unless pool is stopping, this thread does not have
     * its interrupt set.
     *
     * 3. Each task run is preceded by a call to beforeExecute, which
     * might throw an exception, in which case we cause thread to die
     * (breaking loop with completedAbruptly true) without processing
     * the task.
     *
     * 4. Assuming beforeExecute completes normally, we run the task,
     * gathering any of its thrown exceptions to send to afterExecute.
     * We separately handle RuntimeException, Error (both of which the
     * specs guarantee that we trap) and arbitrary Throwables.
     * Because we cannot rethrow Throwables within Runnable.run, we
     * wrap them within Errors on the way out (to the thread‘s
     * UncaughtExceptionHandler).  Any thrown exception also
     * conservatively causes thread to die.
     *
     * 5. After task.run completes, we call afterExecute, which may
     * also throw an exception, which will also cause thread to
     * die. According to JLS Sec 14.20, this exception is the one that
     * will be in effect even if task.run throws.
     *
     * The net effect of the exception mechanics is that afterExecute
     * and the thread‘s UncaughtExceptionHandler have as accurate
     * information as we can provide about any problems encountered by
     * user code.
     *
     * @param w the worker
     */
    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
		    //执行一个死循环,获取第一个任务或者是getTask()方法从队列获取到的任务
			//然后执行任务接口的run方法
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }

总结:

开启一个线程,需要实现Runnable接口的run方法但是该方法需要由虚拟机调起并执行(即我们主动调用start方法后后由虚拟机调用)。但是线程池通过在run方法里面写一个死循环导致线程不会退出,同时通过不断的从队列中获取新的任务并主动调用run方法从而保证了任务是在新的线程中执行。
PS:这导致了一个小区别,使用Thread类时run方法其实是虚拟机发起的调用,而在线程池中实际上是我们主动发起的调用。



知道了线程池能重复执行任务的原理,我们就可以自己模拟实现一个线程池了
1.1 线程池的实现
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;

public class FixedThreadPool {

	private List<Thread> threadList = new LinkedList<Thread>();
	private LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();

	public FixedThreadPool(int threadnum) {
		while (threadnum > 0) {
			threadnum--;
			threadList.add(newThread());
		}
		threadList.forEach(e -> e.start());
	}

	public FixedThreadPool(int threadnum, boolean deamon) {
		while (threadnum > 0) {
			threadnum--;
			threadList.add(newThread());
		}
		threadList.forEach(e -> {
			e.setDaemon(true);
			e.start();
		});
	}

	private Thread newThread() {
		Thread thread = new Thread(new Runnable() {

			@Override
			public void run() {
				Runnable task = null;
				try {
					while (null != (task = queue.take())) {
						task.run();
					}
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

				System.out.println(" 执行结束 ");
			}
		});
		return thread;
	}

	public void submit(Runnable runnable) {
		try {
			queue.put(runnable);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
1.2 正式调用
	public static void main(String[] args) throws CloneNotSupportedException {

		FixedThreadPool tPool = new FixedThreadPool(6);
		for (int i = 0; i < 10; i++) {
			tPool.submit(App::task);
		}

		for (int i = 0; i < 5; i++) {
			tPool.submit(App::task);
		}
	}

如何模拟实现一个线程池

原文:https://www.cnblogs.com/kbhan/p/12592841.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!