当前位置: 移动技术网 > IT编程>开发语言>Java > Java多线程的调度_动力节点Java学院整理

Java多线程的调度_动力节点Java学院整理

2019年07月22日  | 移动技术网IT编程  | 我要评论

有多个线程,如何控制它们执行的先后次序?

        方法一:设置线程优先级。

        java.lang.thread 提供了 setpriority(int newpriority) 方法来设置线程的优先级,但线程的优先级是无法保障线程的执行次序的,优先级只是提高了优先级高的线程获取 cpu 资源的概率。也就是说,这个方法不靠谱。

        方法二:使用线程合并。

        使用 java.lang.thread 的 join() 方法。比如有线程 a,现在当前线程想等待 a 执行完之后再往下执行,那就可以使用 a.join()。一旦线程使用了 a.join(),那么当前线程会一直等待 a 消亡之后才会继续执行。什么时候 a 消亡?a 的 run() 方法执行结束了 a 就消亡了。

        这个方法可以有效地进行线程调度,但却只能局限于等待一个线程的执行调度。如果要等待 n 个线程的话,显然是无能为力了。而且等待线程必须在被等待线程消亡后才得到继续执行的指令,无法做到两个线程真正意义上的并发,灵活性较差。

        方法三:使用线程通信。

        java.lang.object 提供了可以进行线程间通信的 wait 与 notify 、notifyall 等方法。每个 java 对象都有一个隐性的线程锁的概念,通过这个线程锁的概念我们让线程间可以进行通信,各线程不再埋头单干。著名的“生产者-消费者”模型就是基于这个原理实现的。

        这个方法也可以有效地进行线程调度,而且也不仅仅局限于等待一个线程的执行调度,具有很大程度上的灵活性。但操作复杂,不易控制容易造成混乱,程序维护起来也不太方便。

        方法四:使用闭锁。

        闭锁就像一扇门,在先决条件未达成之前这扇门是闭着的,线程无法通过,先决条件达成之后,闭锁打开,线程就可以继续执行了。java.util.concurrent.countdownlatch 是一个很实用的闭锁实现,它提供了 countdown() 和 await() 方法达成线程执行队列,这个方法最适合 m 个线程等待 n 个线程执行结束再执行的情况。首先初始化一个 countdownlatch 对象,比如 countdownlatch donesignal = new countdownlatch(n);该对象具有 n 作为计数阀值,每个被等待线程通过对 donesignal 对象的持有,使用 countdown() 可以将 donesignal 的计数阀值减一;每个等待线程通过对 donesignal 对象的持有,使用 await() 阻塞当前线程,直到 donesignal 计数阀值减为 0,才继续往下执行。

        这个方法也可以有效地进行线程调度,而且比方法三更易于管理,开发者只需控制好 countdownlatch 即可。但线程执行次序管理相对单一,它只是指出当前等待线程的数量,而且 countdownlatch 的初始阀值一旦设置就只能递减下去,无法重置。如需递减过程中进行阀值的重置可以参考 java.util.concurrent.cyclicbarrier。

        不管如何,countdownlatch 对于一定条件下的线程队列的达成还是很有用的。对于复杂环境下的线程管理还是卓有成效的。所以熟悉和把握对它的使用还是很有必要的。

        以下是一个实际项目中 countdownlatch 的使用的例子:

 private map<long,decryptsignalandpath> afterdecryptfilepathmap = new hashmap<long,decryptsignalandpath>();//todo 注意容器垃圾数据的清理工作 
 class decryptrunnable implements runnable { 
   private serverfilebean serverfile; 
   private long fid;//指向解密文件 
   private countdownlatch decryptsignal; 
   protected decryptrunnable(long fid, serverfilebean serverfile, countdownlatch decryptsignal) { 
     this.fid = fid; 
     this.serverfile = serverfile; 
     this.decryptsignal = decryptsignal; 
   } 
   @override 
   public void run() { 
     //开始解密 
     string afterdecryptfilepath = null; 
     decryptsignalandpath decryptsignalandpath = new decryptsignalandpath(); 
     decryptsignalandpath.setdecryptsignal(decryptsignal); 
     afterdecryptfilepathmap.put(fid, decryptsignalandpath); 
     afterdecryptfilepath = decryptfile(serverfile); 
     decryptsignalandpath.setafterdecryptfilepath(afterdecryptfilepath); 
     decryptsignal.countdown();//通知所有阻塞的线程 
   } 
    
 } 
 class decryptsignalandpath { 
   private string afterdecryptfilepath; 
   private countdownlatch decryptsignal; 
   public string getafterdecryptfilepath() { 
     return afterdecryptfilepath; 
   } 
   public void setafterdecryptfilepath(string afterdecryptfilepath) { 
     this.afterdecryptfilepath = afterdecryptfilepath; 
   } 
   public countdownlatch getdecryptsignal() { 
     return decryptsignal; 
   } 
   public void setdecryptsignal(countdownlatch decryptsignal) { 
     this.decryptsignal = decryptsignal; 
   } 
 } 

        需要先执行的,被等待线程在这里加入:

 countdownlatch decryptsignal = new countdownlatch(1); 
 new thread(new decryptrunnable(fid, serverfile, decryptsignal)).start();//无需拿到新线程句柄,由 countdownlatch 自行跟踪 
 try { 
   decryptsignal.await(); 
 } catch (interruptedexception e) { 
   // todo auto-generated catch block 
 } 

        需要后执行,等待的线程可以这样加入:

countdownlatch decryptsignal = afterdecryptfilepathmap.get(fid).getdecryptsignal();   
 try { 
   decryptsignal.await(); 
 } catch (interruptedexception e) { 
   // todo auto-generated catch block 
 } 

        当然,这也仅仅只是一个简单的 countdownlatch 的使用展示,对于 countdownlatch 来说有点大材小用了,因为它可以胜任更复杂的多线程环境。示例中的案例完全可以使用线程通信进行搞定。因为 countdownlatch 的阀值初始为 1,所以这里甚至完全可以使用方法二所说的线程的合并进行取代。

        如果读者觉得以上示例不够清晰,也可以参考 jdk api 提供的 demo,这个清晰明了:

 class driver2 { // ... 
  void main() throws interruptedexception { 
   countdownlatch donesignal = new countdownlatch(n); 
   executor e = ... 
  
   for (int i = 0; i < n; ++i) // create and start threads 
    e.execute(new workerrunnable(donesignal, i)); 
  
   donesignal.await();      // wait for all to finish 
  } 
 } 
  
 class workerrunnable implements runnable { 
  private final countdownlatch donesignal; 
  private final int i; 
  workerrunnable(countdownlatch donesignal, int i) { 
   this.donesignal = donesignal; 
   this.i = i; 
  } 
  public void run() { 
   try { 
    dowork(i); 
    donesignal.countdown(); 
   } catch (interruptedexception ex) {} // return; 
  } 
  
  void dowork() { ... } 
 } 

以上所述是小编给大家介绍的java多线程的调度,希望对大家有所帮助

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网