胡思乱想JNI与线程池的维护

Posted longfurcat

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了胡思乱想JNI与线程池的维护相关的知识,希望对你有一定的参考价值。

JNI中,C/C++代码里创建的资源不由Java GC处理,故这里的资源必须由C/C++代码明确释放。在JNI中,C/C++回调Java的方法是调用一个CallXXMethod函数来实现的,如果回调的方法结束,C/C++执行下一行代码。

故猜测,由C/C++创建的OS线程应该会在运行完run方法后释放,不然好像也没有其他合适的时间点来对线程进行释放了。因为按照语义的话,既然线程的任务已经完成,那线程还留着干什么,就应该被释放。

有些时候,我们需要维护一个线程池来减少创建和释放线程的开销,让一些线程完成当前任务后被收回到线程池,等待接受下一个任务。根据前面的猜测,run方法结束后,OS线程将被释放。我们要维护线程池,就是不能让线程被释放。所以我们就要阻止run方法返回。当Thread.run把target.run执行完的时候,利用wait挂起线程。直到有新的target,才唤醒当前线程。

以下是我在旧版的《Thinking in Enterprise Java》中看到的线程池的实现。

public class Worker extends Thread { //工作者线程
    public static final Logger logger = Logger.setLogger("Worker"); //类日志
    private String workerId; //工作者ID
    private Runnable task;   //任务对象
    private ThreadPool threadPool;  //线程池引用,方便操作。

    static { //静态块,配置logger
        try {
            logger.setUseParentHandlers(false);
            FileHandler ferr = new FileHandler("WorkerErr.log");
            ferr.setFormatter(new SimpleFormatter());
            logger.addHandler(ferr);
        } catch(IOException e) {
            System.out.println("Logger not initialized.");
        }
    }

    public Worker(String id, ThreadPool pool) {
        workerId = id;
        threadPool = pool;
        start();    //创建即启动
    }

    public void setTask(Runnable t) { //这里放入新任务,并唤醒线程,进入就绪队列。
        task = t;
        synchronized(this) {
            notify(); //wait、notify方法都必须获取对应的锁
        }
    }

    public void run() {
        try {
            while(!threadPool.isStopped()) { //如果线程池未停止工作,此线程不释放
                synchronized(this) {
                    if(task != null) {
                        try {
                            task.run(); //执行任务
                        } catch(Exception e) {
                            logger.log(Level.SERVER, "Exception in source Runnable task", e);
                        }
                        threadPool.putWorker(this); //完成当前任务,回收到线程池
                    }
                    wait(); //完成任务或无任务时,挂起线程。免得空循环浪费时间片。

                }
            }
            //跳出循环,意味着线程池结束工作,此线程也将停止工作
            System.out.println(this + " Stopped");
        } catch(InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public String toString() {
        return "Worker: " + workerId;
    }

}
public class ThreadPool extends Thread { //线程池, 同样是一个线程,负责接收和分配任务
    private static final int DEFAULT_NUM_WORKERS = 5; //默认线程池大小为5
    private LinkedList workerPool = new LinkedList(); //空闲线程列表 
    private LinkedList taskQueue = new LinkedList();  //任务列表
    private boolean stopped = false; //线程池的工作状态
    
    public ThreadPool() { //默认构造方法,线程池大小默认
        this(DEFAULT_NUM_WORKERS);
    }

    public ThreadPool(int numOfWorkers) { //自定义线程池大小
        for(int i=0;i<numOfWorkers;i++){
            workerPool.add(new Worker("" + i, this));
        }
        start(); //创建即启动
    }

    public void run() { //分发任务
        try {
            while(!stopped) {
                if(taskQueue.isEmpty()) { //如果任务队列为空,挂起当前线程。 也就是暂停线程池的分发任务的工作
                    synchronized(taskQueue) {
                        taskQueue.wait(); //不管调用哪个对象的wait方法,都是挂起当前执行它的线程。
                    }
                } else if(workerPool.isEmpty()) { //如果没有空闲的线程,则暂停线程池的工作。
                    synchronized(workerPool) {
                        workerPool.wait();
                    }
                }
                //有任务,且有空闲线程的情况  => 从空闲线程中取出一个线程,让其负责任务队列中的一个任务
                getWorker().setTask((Runnable)taskQueue.removeLast());
            }
        } catch(InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public void addTask(Runnable task) {
        synchronized(taskQueue) {
            taskQueue.addFirst(task);
            taskQueue.notify(); //通知已有新任务,如果前面线程因无任务被挂起,这个操作将唤醒线程
        }
    }

    public void putWorker(Worker worker) {
        synchronized(workerPool) {
            workerPool.addFirst(worker);
            workerPool.notify(); //通知已有新空闲线程,如果前面线程因无空闲工作者线程被挂起,此操作将唤醒线程
        }
    }

    public Worker getWorker() {
        return (Worker) workerPool.removeLast(); //取出一个空闲线程,并从列表中移除。
    }

    public boolean isStopped() {
        return stopped;
    }

    public void stopThreads() { //关闭线程池
        stopped = true;
        Iterator it = workerPool.Iterator();
        //这里唤醒挂起的工作者线程,使得它醒来并发现ThreadPool已关闭,并结束run方法 => 释放OS线程
        while(it.hasNext()) { 
            Worker w = (Worker)it.next();
            synchronized(w) {
                w.notify();
            }
        }
    }

}

对这个代码的认识,在注释里已经表现的很清楚了。另外,我还觉得,ThreadPool和Woker的关系有点观察者模式的味道,Woker是观察者,ThreadPool是被观察者/主题。不过,与标准的观察者模式不同的是,ThreadPool接受到新任务(发生了变化),并没有通知所有Worker。

 

以上是关于胡思乱想JNI与线程池的维护的主要内容,如果未能解决你的问题,请参考以下文章

ThreadPool线程池的关注点

Java四种线程池的学习与总结

Linux线程池

IDEA对新建java线程池的建议

newCacheThreadPool()newFixedThreadPool()newScheduledThreadPool()newSingleThreadExecutor()自定义线程池(代码片段

JUC学习--线程池的学习