Java 线程池详解以及相关问题

一个线程池至少应该具有以下几个方面的功能:

1.提供一个任务接口以便用户加入任务
由于Java不支持方法指针,所以操作(方法)只能绑定在对象上,将拥有操作的队象放入队列中,以便
工作线程能按一定策略获取对象然后执行其上的方法.

这里需要有两个组件,一是规定操作的任务接口:

interface ITask{
 public void task();
}
一是存放操作的容器.容器可以根据调度策略来选择或自己实现,比如一个最简单的FIFO策略的容器
可以用LinkedList来实现.需要注意的是对这个容器的存取需要同步:
class TaskList{
    private LinkedList<ITask> tl = new LinkedList();
    public synchronized void addTask(ITask task){
        this.tl.add(task);

//notifyAll();
    }
    //加上addFistTask和addLastTask

    public synchronized ITask getTask(){
 if(this.size() <= 0)
 wait();
        return this.tl.poll();
    }
    //加上getFistTask和getLastTask

    public synchronized int getCount(){
 return this.tl.size();
    }
    public synchronized void removeAll(){
 this.tl.clear();
    }

}

加上addFistTask和addLastTask/getFistTask和getLastTask实现就可以实现简单的优先级调度.

2.工作线程
我们把用来执行用户任务的线程称为工作线程,工作线程就是不断从队列中获取任务对象并执行对象
上的业务方法:

class WorkThread extends Thread{
    private TaskList list;
    //多个工作线程共同从一个任务队列中获取任务,所以要从外面传入一个任务队列.
    public WorkThread(String name,TaskList list){
      super(name);
      this.list = list;
    }
    public void run(){
      while(true){     
        ITask task = null;

        task = list.getTask();
        if(task != null) task.task();
      }
    }
}

在基础知识部分已经介绍了对线程状态的标记控制,请参照前面的知识自己加入对线线程状态的判断.

完成了这几个基础部分,就需要有一个对工作线程的调度线程.完成以下几个功能:
1.生成需要的工作线程.由于创建线程需要一定的开销,一定要注意所创建的所有线程不能超一个设定
的最大值.建议最大值不要超25.

2.动态自适应调整集合中线程数.当有太多的线程处于闲置状态时(队列中没有任务),应该按一定比例
销毁闲置了一定时的线程.如果队列中任务队列积压太多而工作线程总数没有超最大线程数时应该及时
创建工作线程直至达到是大值.

3.需要一个专门的后台线程定时扫描队列中任务与正在工作的线程总数,闲置的线程总数.

以上功能有不同优化方法实现,可以参考JDK的线程池实现. 

 

java线程池中,线程重用问题解释

I think I understood what is confuzzabling you so here's my longer answer: the terminology is a tiny bit misleading (obviously, or you wouldn't ask that question specifically putting the emphasis on 'reuse'):

How do thread pools 'reuse' threads?

What is happening is that a single thread can be used to process several tasks (typically passed as Runnable, but this depend on your 'executor' framework: the default executors accepts Runnable, but you could write your own "executor" / thread-pool accepting something more complex than a Runnable [like, say, a CancellableRunnable]).

Now in the default ExecutorService implementation if a thread is somehow terminated while still in use, it is automatically replaced with a new thread, but this is not the 'reuse' they're talking about. There is no "reuse" in this case.

So it is true that you cannot call start() on a Java Thread twice but you can pass as many Runnable as you want to an executor and each Runnable's run() method shall be called once.

You can pass 30 Runnable to 5 Java Thread and each worker thread may be calling, for example, run() 6 times (practically there's not guarantee that you'll be executing exactly 6 Runnable per Thread but that is a detail).

In this example start() would have been called 6 times. Each one these 6 start() will call exactly oncethe run() method of each Thread:

From Thread.start() Javadoc:

 * Causes this thread to begin execution; the Java Virtual Machine 
 * calls the <code>run</code> method of this thread.

BUT then inside each Thread's run() method Runnable shall be dequeued and the run() method of each Runnable is going to be called. So each thread can process several Runnable. That's what they refer to by "thread reuse".

One way to do your own thread pool is to use a blocking queue on to which you enqueue runnables and have each of your thread, once it's done processing the run() method of a Runnable, dequeue the next Runnable (or block) and run its run() method, then rinse and repeat.

I guess part of the confusion (and it is a bit confusing) comes from the fact that a Thread takes a Runnable and upon calling start() the Runnable 's run() method is called while the default thread pools also take Runnable.

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

The run method of threads in a thread pool does not consist only of running a single task. The runmethod of a thread in a thread pool contains a loop. It pulls a task off of a queue, executes the task (which returns back to the loop when it is complete), and then gets the next task. The run method doesn't complete until the thread is no longer needed.

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            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);
        }
    }

 

posted @ 2016-04-14 00:37  程序猿进化之路  阅读(136)  评论(0)    收藏  举报