胡思乱想jni与线程池的维护(代码片段)

longfurcat longfurcat     2023-01-12     630

关键词:

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。

 

线程池的基础与操作(代码片段)

...概念使用ThreadPoolExecutor类:包含了五个参数intcorePoolSize(线程池的基本大小),intmaximumPoolSize(最大线程池容量),longkeepAliveTime(存活时间),TimeUnitunit(时间单位),BlockingQueue<Runnable>workQueue(任务队列)    corePoolSize:当... 查看详情

22jni-动态注册与jni线程(代码片段)

AndroidNdk学习笔记(目录)//1静态注册native函数publicnativevoidstaticRegister();//---------------------------静态注册---------------------------------extern"C"JNIEXPORTvoidJNICALLJava_com_cn_mynat 查看详情

回顾线程与线程池(代码片段)

线程、进程、线程池的回顾1.线程与进程2.线程的状态3.线程的创建方式4.start()和run()方法的区别5.线程池的定义6.线程池的创建7.线程池的核心参数8.线程池的工作原理9.sleep()、wait()、yield()、join()10.synchronized()和Lock的区别10.LockSuppor... 查看详情

一文简单理解java线程池的问题(代码片段)

线程池:一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。... 查看详情

java线程池的底层实现与使用(代码片段)

...开发的时候,为了充分利用系统资源,我们通常会进行多线程开发,实现起来非常简单,需要使用线程的时候就去创建一个线程(继承Thread类、实现Runnable接口、使用Callable和Future),但是这样也有一点问题,就是如果并发的线... 查看详情

线程池的原理与使用场景(代码片段)

 1、线程池简介:   多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。       假设一个服务器完成一项任务所... 查看详情

线程池的理解与简单实现(代码片段)

池由于服务器的硬件资源“充裕”,那么提高服务器性能的一个很直接的方法就是以空间换时间,即“浪费”服务器的硬件资源,以换取其运行效率。这就是池的概念。池是一组资源的集合,这组资源在服务器启动之初就被创建... 查看详情

linux/设计模式线程池的实现(代码片段)

线程池任务类线程安全的结构线程池类测试线程池是一种非常典型的生产消费者模型。我们在利用线程处理任务的时候,线程过多会带来调度开销,有大量的时间用去创建和处理线程,进而影响缓存局部性和整体性能... 查看详情

并发7-线程池(代码片段)

用优雅的方式理解和使用线程池线程池的目的(1)减少系统维护线程的开销(2)解耦,运行和创建分开(3)线程可以复用线程池的使用(1)接口Executor 提供方法execute(Runnable)执行线程命令(2)接口ExecutorService提供方法shutdo... 查看详情

代理池的维护(代码片段)

介绍代理池的维护一、准备工作安装redis数据库并启动服务,另外还需安装atihttp,requests,redis-py,pyquery,flask二、代理池的架构分为4个模块:存储模块,获取模块,检测模块,借口模块、1、存储模块:使用Redis的有序集合,用来做代... 查看详情

线程池的基本概念(代码片段)

线程池,是一种线程的使用模式,它为了降低线程使用中频繁的创建和销毁所带来的资源消耗与代价。通过创建一定数量的线程,让他们时刻准备就绪等待新任务的到达,而任务执行结束之后再重新回来继续待命。这就是线程池... 查看详情

维护自己的线程池的库有啥影响?

】维护自己的线程池的库有啥影响?【英文标题】:Whatistheimpactoflibrariesthatmaintaintheirownthreadspool?维护自己的线程池的库有什么影响?【发布时间】:2015-05-1117:54:24【问题描述】:我在我们的项目中使用GoogleGuava和NetflixHystrix库。... 查看详情

回顾线程与线程池(代码片段)

线程、进程、线程池的回顾1.线程与进程2.线程的状态3.线程的创建方式4.start()和run()方法的区别5.线程池的定义6.线程池的创建7.线程池的核心参数8.线程池的工作原理9.sleep()、wait()、yield()、join()10.synchronized()和Lock的区别10.LockSuppor... 查看详情

回顾线程与线程池(代码片段)

线程、进程、线程池的回顾1.线程与进程2.线程的状态3.线程的创建方式4.start()和run()方法的区别5.线程池的定义6.线程池的创建7.线程池的核心参数8.线程池的工作原理9.sleep()、wait()、yield()、join()10.synchronized()和Lock的区别10.LockSuppor... 查看详情

浅理解java中的线程池(代码片段)

浅理解java中的线程池线程池(threadpool)1.线程池的概念2.线程池的工作机制3.使用线程池的原因4.线程池相关概念5.线程池的主要参数6.java中提供的线程池6.1newCachedThreadPool6.2newFixedThreadPool6.3newSingleThreadExecutor6.4newScheduledThrea... 查看详情

线程池的使用(代码片段)

线程池1.介绍多线程缺点:处理任务的线程创建和销毁都非常的耗时间并消耗资源多线程之间的切换也会非常耗时并消耗资源采用线程池的好处使用时线程已经存在,消除了线程创建的消耗通过设置线程数目,防止资源不足2.Threa... 查看详情

threadpool线程池的关注点

publicclassTestThreadPool{//线程池维护线程的最少数量privatestaticfinalintCOREPOOLSIZE=2;//线程池维护线程的最大数量privatestaticfinalintMAXINUMPOOLSIZE=5;//线程池维护线程所允许的空闲时间privatestaticfinallongKEEPALIVETIME=4;//线程池维护线程所允许的空... 查看详情

线程池的使用(代码片段)

线程池jdk1.5后官方提供线程池 创建一个线程类/***@Classnamerunnabledemo*@DescriptionTODO*@Date2020/11/1111:59*@CreatedbyAdministrator*/publicclassrunnabledemoimplementsRunnable@Overridepublicvoidrun()System.out.pr 查看详情