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》中看到的线程池的实现。

  1. public class Worker extends Thread { //工作者线程
  2. public static final Logger logger = Logger.setLogger("Worker"); //类日志
  3. private String workerId; //工作者ID
  4. private Runnable task; //任务对象
  5. private ThreadPool threadPool; //线程池引用,方便操作。
  6.  
  7. static { //静态块,配置logger
  8. try {
  9. logger.setUseParentHandlers(false);
  10. FileHandler ferr = new FileHandler("WorkerErr.log");
  11. ferr.setFormatter(new SimpleFormatter());
  12. logger.addHandler(ferr);
  13. } catch(IOException e) {
  14. System.out.println("Logger not initialized.");
  15. }
  16. }
  17.  
  18. public Worker(String id, ThreadPool pool) {
  19. workerId = id;
  20. threadPool = pool;
  21. start(); //创建即启动
  22. }
  23.  
  24. public void setTask(Runnable t) { //这里放入新任务,并唤醒线程,进入就绪队列。
  25. task = t;
  26. synchronized(this) {
  27. notify(); //wait、notify方法都必须获取对应的锁
  28. }
  29. }
  30.  
  31. public void run() {
  32. try {
  33. while(!threadPool.isStopped()) { //如果线程池未停止工作,此线程不释放
  34. synchronized(this) {
  35. if(task != null) {
  36. try {
  37. task.run(); //执行任务
  38. } catch(Exception e) {
  39. logger.log(Level.SERVER, "Exception in source Runnable task", e);
  40. }
  41. threadPool.putWorker(this); //完成当前任务,回收到线程池
  42. }
  43. wait(); //完成任务或无任务时,挂起线程。免得空循环浪费时间片。
  44.  
  45. }
  46. }
  47. //跳出循环,意味着线程池结束工作,此线程也将停止工作
  48. System.out.println(this + " Stopped");
  49. } catch(InterruptedException e) {
  50. throw new RuntimeException(e);
  51. }
  52. }
  53.  
  54. public String toString() {
  55. return "Worker: " + workerId;
  56. }
  57.  
  58. }
  1. public class ThreadPool extends Thread { //线程池, 同样是一个线程,负责接收和分配任务
  2. private static final int DEFAULT_NUM_WORKERS = ; //默认线程池大小为5
  3. private LinkedList workerPool = new LinkedList(); //空闲线程列表
  4. private LinkedList taskQueue = new LinkedList(); //任务列表
  5. private boolean stopped = false; //线程池的工作状态
  6.  
  7. public ThreadPool() { //默认构造方法,线程池大小默认
  8. this(DEFAULT_NUM_WORKERS);
  9. }
  10.  
  11. public ThreadPool(int numOfWorkers) { //自定义线程池大小
  12. for(int i=;i<numOfWorkers;i++){
  13. workerPool.add(new Worker("" + i, this));
  14. }
  15. start(); //创建即启动
  16. }
  17.  
  18. public void run() { //分发任务
  19. try {
  20. while(!stopped) {
  21. if(taskQueue.isEmpty()) { //如果任务队列为空,挂起当前线程。 也就是暂停线程池的分发任务的工作
  22. synchronized(taskQueue) {
  23. taskQueue.wait(); //不管调用哪个对象的wait方法,都是挂起当前执行它的线程。
  24. }
  25. } else if(workerPool.isEmpty()) { //如果没有空闲的线程,则暂停线程池的工作。
  26. synchronized(workerPool) {
  27. workerPool.wait();
  28. }
  29. }
  30. //有任务,且有空闲线程的情况 => 从空闲线程中取出一个线程,让其负责任务队列中的一个任务
  31. getWorker().setTask((Runnable)taskQueue.removeLast());
  32. }
  33. } catch(InterruptedException e) {
  34. throw new RuntimeException(e);
  35. }
  36. }
  37.  
  38. public void addTask(Runnable task) {
  39. synchronized(taskQueue) {
  40. taskQueue.addFirst(task);
  41. taskQueue.notify(); //通知已有新任务,如果前面线程因无任务被挂起,这个操作将唤醒线程
  42. }
  43. }
  44.  
  45. public void putWorker(Worker worker) {
  46. synchronized(workerPool) {
  47. workerPool.addFirst(worker);
  48. workerPool.notify(); //通知已有新空闲线程,如果前面线程因无空闲工作者线程被挂起,此操作将唤醒线程
  49. }
  50. }
  51.  
  52. public Worker getWorker() {
  53. return (Worker) workerPool.removeLast(); //取出一个空闲线程,并从列表中移除。
  54. }
  55.  
  56. public boolean isStopped() {
  57. return stopped;
  58. }
  59.  
  60. public void stopThreads() { //关闭线程池
  61. stopped = true;
  62. Iterator it = workerPool.Iterator();
  63. //这里唤醒挂起的工作者线程,使得它醒来并发现ThreadPool已关闭,并结束run方法 => 释放OS线程
  64. while(it.hasNext()) {
  65. Worker w = (Worker)it.next();
  66. synchronized(w) {
  67. w.notify();
  68. }
  69. }
  70. }
  71.  
  72. }

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

【胡思乱想】JNI与线程池的维护的更多相关文章

  1. 分享一个自制的 .net线程池

    扯淡 由于项目需求,需要开发一些程序去爬取一些网站的信息,算是小爬虫程序吧.爬网页这东西是要经过网络传输,如果程序运行起来串行执行请求爬取,会很慢,我想没人会这样做.为了提高爬取效率,必须使用多线程并 ...

  2. CompletionService/ExecutorCompletionService/线程池/concurrent包

    线程池 线程池的基本思想:线程频繁的创建.销毁会极大地占用系统资源,为了减少系统在创建销毁线程时的开销,线程池应运而生.线程池包括多个已创建的线程,当有任务要在新线程中执行时,将任务提交给线程池,线程 ...

  3. JAVA基础知识之多线程——线程池

    线程池概念 操作系统或者JVM创建一个线程以及销毁一个线程都需要消耗CPU资源,如果创建或者销毁线程的消耗源远远小于执行一个线程的消耗,则可以忽略不计,但是基本相等或者大于执行线程的消耗,而且需要创建 ...

  4. Linux平台下线程池的原理及实现

    转自:http://blog.csdn.net/lmh12506/article/details/7753952 前段时间在github上开了个库,准备实现自己的线程池的,因为换工作的事,一直也没有实 ...

  5. 【温故而知新-万花筒】C# 异步编程 逆变 协变 委托 事件 事件参数 迭代 线程、多线程、线程池、后台线程

    额基本脱离了2.0 3.5的时代了.在.net 4.0+ 时代.一切都是辣么简单! 参考文档: http://www.cnblogs.com/linzheng/archive/2012/04/11/2 ...

  6. .Net多线程编程—Parallel LINQ、线程池

    Parallel LINQ 1 System.Linq.ParallelEnumerable 重要方法概览: 1)public static ParallelQuery<TSource> ...

  7. Java多线程之Executor框架和手写简易的线程池

    目录 Java多线程之一线程及其基本使用 Java多线程之二(Synchronized) Java多线程之三volatile与等待通知机制示例 线程池 什么是线程池 线程池一种线程使用模式,线程池会维 ...

  8. C#并行编程(2):.NET线程池

    线程 Thread 在总结线程池之前,先来看一下.NET线程. .NET线程与操作系统(Windows)线程有什么区别? .NET利用Windows的线程处理功能.在C#程序编写中,我们首先会新建一个 ...

  9. Java编程的逻辑 (78) - 线程池

    ​本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http: ...

随机推荐

  1. MySQL中数据的基本查询方式

    1.查询所有列 select * from 表名称; 2.查询指定列 select 字段名,字段名,字段名 from 表名称; 3.查询时添加常量列(临时备注) select 字段名,字段名,字段名, ...

  2. 模式PK:命令模式VS策略模式

    1.概述 命令模式和策略模式的类图确实很相似,只是命令模式多了一个接收者(Receiver)角色.它们虽然同为行为类模式,但是两者的区别还是很明显的.策略模式的意图是封装算法,它认为“算法”已经是一个 ...

  3. 14:super关键字

    本小节知识点: 1.super基本概念 2.super的作用 1.super基本概念 super是个编译器的指令符号,只是告诉编译器在执行的时候,去调谁的方法. self是一个隐私参数; self r ...

  4. lca tarjin

    这个算法  我个人认为是  遍历每一个点把它当成一些询问的最近祖先 1 2 3 4 5 6 low是并差集,vis是是否访问过,访问过为true,没有为false: 假设询问是(4,4),(4,5), ...

  5. hdu 5094 状压bfs+深坑

    http://acm.hdu.edu.cn/showproblem.php?pid=5094 给出n*m矩阵 给出k个障碍,两坐标之间存在墙或门,门最多10种,状压可搞 给出s个钥匙位置及编号,相应的 ...

  6. Scala_特质

    特质 特质概述 Java中提供了接口,允许一个类实现任意数量的接口 在Scala中没有接口的概念,而是提供了“特质(trait) ”,它不仅实 现了接口的功能,还具备了很多其他的特性 Scala的特质 ...

  7. file新建文件及文件夹

    1.获取包名的根目录:mRootPath = getFilesDir().getParent(); // ====mRootPath===/data/data/com.yoyu.file  获取SD卡 ...

  8. verilog中defparam的用法 (verilog调用底层模块(只改变)参数的传递)

    当一个模块引用另外一个模块时,高层模块可以改变低层模块用parameter定义的参数值,改变低层模块的参数值可采用以下两种方式: 1)defparam 重定义参数 语法:defparam path_n ...

  9. DevExpress GridControl 关于使用CardView的一点小结

    最近项目里需要显示商品的一系列图片,打算用CardView来显示,由于第一次使用,遇到许多问题,发现网上这方面的资源很少,所以把自己的一点点实际经验小结一下,供自己和大家以后参考. 1.选择CardV ...

  10. 如何在js里引用php变量

    如何在js里面引用php的变量 php代码------------------------------------------- js代码------------------------------- ...