• 03.Java多线程并发库API使用2


    1.多个线程之间共享数据的方式探讨

    1、如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做。

    2、如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,有如下两种方式来实现这些Runnable对象之间的数据共享:

    • 将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。
    • 将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable对象调用外部类的这些方法。

    上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。

    总之,要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥和通信。

    极端且简单的方式,即在任意一个类中定义一个static的变量,这将被所有线程共享。

    示例代码

     1 package com.chunjiangchao.thread;
     2 /**
     3  * 多线程之间数据共享
     4  * @author chunjiangchao
     5  *
     6  */
     7 public class MultiThreadShareDataDemo {
     8 
     9     public static void main(String[] args) {
    10         Data data = new Data();
    11         new Thread(new IncrementRunnable(data)).start();
    12         new Thread(new DecrementtRunnable(data)).start();
    13         
    14         final Data data2 = new Data();
    15         new Thread(new Runnable() {
    16             
    17             @Override
    18             public void run() {
    19                 data2.increment();
    20             }
    21         }).start();
    22         new Thread(new Runnable() {
    23             
    24             @Override
    25             public void run() {
    26                 data2.decrement();
    27             }
    28         }).start();
    29     }
    30     //对共享数据进行增加
    31     private static class IncrementRunnable implements Runnable{
    32         private Data data ;
    33         public IncrementRunnable(Data data){
    34             this.data = data;
    35         }
    36         public void run() {
    37             data.increment();
    38         }
    39     }
    40     //对共享数据进行减少
    41     private static class DecrementtRunnable implements Runnable{
    42         private Data data ;
    43         public DecrementtRunnable(Data data){
    44             this.data = data;
    45         }
    46         public void run() {
    47             data.decrement();
    48         }
    49     }
    50     
    51     
    52     //共享数据
    53     private static class Data{
    54         private int temp=0;
    55         public synchronized  void increment(){
    56             temp++;
    57             System.out.println(Thread.currentThread()+"中temp的值为:"+temp);
    58         }
    59         public synchronized void decrement(){
    60             temp--;
    61             System.out.println(Thread.currentThread()+"中temp的值为:"+temp);
    62         }
    63     } 
    64 
    65 }

    2.java5线程并发库的应用(Executors)

    static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。(创建固定线程池)

    如果在这个线程池里面,创建的线程为3个线程,但是交给的任务时10个任务的话,那么,线程池里面的线程就会运行完3个线程后,接着运行3个线程,直到所有的线程运行完毕。

     List<Runnable> shutdownNow()试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。

    shutdown()启动一次顺序关闭,执行以前提交的任务,但不接受新任务。

    static ExecutorService newCachedThreadPool()创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。(动态创建线程池,有多少任务,自动创建多少线程)

    static ExecutorService  newSingleThreadExecutor():创建单个线程,如果线程死掉了,它会自动找个替补线程补上去。(如何实现线程死掉之后重新启动)?

    static ScheduledExecutorService newScheduledThreadPool(int corePoolSize):创建一个定时线程池

    实例代码:

     1 package com.chunjiangchao.thread;
     2 
     3 import java.util.Date;
     4 import java.util.concurrent.ExecutorService;
     5 import java.util.concurrent.Executors;
     6 import java.util.concurrent.TimeUnit;
     7 
     8 /**
     9  * 线程并发库,线程池的使用
    10  * @author chunjiangchao
    11  *
    12  */
    13 public class ExecuterDemo {
    14 
    15     public static void main(String[] args) {
    16 //        ExecutorService threadPool = Executors.newFixedThreadPool(3);//开了固定的三个线程
    17 //        ExecutorService threadPool = Executors.newCachedThreadPool();//开了10个线程
    18         ExecutorService threadPool = Executors.newSingleThreadExecutor();//开了一个固定的线程
    19         for(int i=0;i<10;i++){
    20             final int loop = i;
    21             threadPool.execute(new Runnable(){
    22                 public void run() {
    23                     try {
    24                         Thread.sleep(1000);
    25                     } catch (InterruptedException e) {
    26 //                        e.printStackTrace();
    27                     }
    28                     System.out.println(Thread.currentThread().getName()+" outer "+loop);
    29                 }
    30                 
    31             });
    32         }
    33         /*
    34              shutdownNow执行的结果为:
    35              pool-1-thread-3 outer 
    36             pool-1-thread-1 outer 
    37             pool-1-thread-2 outer * */
    38 //        threadPool.shutdownNow();
    39         /*shutdown会执行完所有已经提交的任务,不会处理shutdown后提交的任务,而且在后面提交Runnable的时候,
    40          * 会抛出异常java.util.concurrent.RejectedExecutionException*/
    41         threadPool.shutdown();
    42 //        threadPool.execute(new Runnable(){
    43 //
    44 //            @Override
    45 //            public void run() {
    46 //                System.out.println("不会进行处理");
    47 //            }
    48 //            
    49 //        });
    50         //实现定时器效果
    51         Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(new Runnable(){
    52 
    53             @Override
    54             public void run() {
    55                 System.out.println("执行定时器结果"+new Date().toLocaleString());
    56             }
    57             
    58         }, 2, 4, TimeUnit.SECONDS);//每隔4s玩一次
    59     }
    60 
    61 }

    3.Callable&Future

    Future取得的结果类型和Callable返回的结果类型必须一致,这是通过泛型来实现的。

    Callable要采用ExecutorSevice的submit方法提交,返回的future对象可以取消任务。

    CompletionService用于提交一组Callable任务,其take方法返回已完成的一个Callable任务对应的Future对象。

    take()   获取并移除表示下一个已完成任务的 Future,如果目前不存在这样的任务,则等待

    示例代码

      1 package com.chunjiangchao.thread;
      2 
      3 import java.util.Date;
      4 import java.util.concurrent.Callable;
      5 import java.util.concurrent.ExecutionException;
      6 import java.util.concurrent.Executor;
      7 import java.util.concurrent.ExecutorCompletionService;
      8 import java.util.concurrent.ExecutorService;
      9 import java.util.concurrent.Executors;
     10 import java.util.concurrent.Future;
     11 
     12 /**
     13  * Callable&Future的使用
     14  * @author chunjiangchao
     15  *
     16  */
     17 public class CallableAndFutureDemo {
     18 
     19     public static void main(String[] args) {
     20         ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
     21         //提交单一任务
     22         Future<String> submit = newSingleThreadExecutor.submit(new Callable<String>(){
     23 
     24             @Override
     25             public String call() throws Exception {
     26                 printTime();
     27                 mSleep(3000);
     28                 printTime();
     29                 return "我这有返回值,你看看是不是";
     30             }
     31             
     32         });
     33         mSleep(500);
     34         try {
     35             String string = submit.get();
     36             System.out.println(string);
     37         } catch (InterruptedException | ExecutionException e) {
     38             e.printStackTrace();
     39         }
     40 //        submit.cancel(true);//可以对任务进行取消
     41         //提交多个任务
     42         Executor executor = Executors.newCachedThreadPool(); 
     43         ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(executor);
     44         for(int i=0;i<10;i++){
     45             final int loop = i;
     46             completionService.submit(new Callable<String>(){
     47 
     48                 @Override
     49                 public String call() throws Exception {
     50                     mSleep(1000*loop);
     51                     return "提交多任务有返回结果"+loop;
     52                 }
     53                 
     54             });
     55         }
     56         for(int i=0;i<10;i++){
     57             try {
     58                 Future<String> result = completionService.take();
     59                 printTime();
     60                 System.out.println(result.get());
     61             } catch (InterruptedException e) {
     62                 e.printStackTrace();
     63             } catch (ExecutionException e) {
     64                 e.printStackTrace();
     65             }
     66         }
     67         /*
     68          *     打印 结果如下
     69              2016-4-18 11:57:46
     70             2016-4-18 11:57:49
     71             我这有返回值,你看看是不是
     72             2016-4-18 11:57:49
     73             提交多任务有返回结果0
     74             2016-4-18 11:57:50
     75             提交多任务有返回结果1
     76             2016-4-18 11:57:51
     77             提交多任务有返回结果2
     78             2016-4-18 11:57:52
     79             提交多任务有返回结果3
     80             2016-4-18 11:57:53
     81             提交多任务有返回结果4
     82             2016-4-18 11:57:54
     83             提交多任务有返回结果5
     84             2016-4-18 11:57:55
     85             提交多任务有返回结果6
     86             2016-4-18 11:57:56
     87             提交多任务有返回结果7
     88             2016-4-18 11:57:57
     89             提交多任务有返回结果8
     90             2016-4-18 11:57:58
     91             提交多任务有返回结果9
     92          */
     93         
     94     }
     95     private static void mSleep(long time){
     96         try {
     97             Thread.sleep(time);
     98         } catch (InterruptedException e) {
     99             e.printStackTrace();
    100         }
    101     }
    102     private static void printTime(){
    103         System.out.println(new Date().toLocaleString());
    104     }
    105 
    106 }

    4.java5的线程锁技术

    Lock的使用

     1 package com.chunjiangchao.thread;
     2 
     3 import java.util.concurrent.locks.Lock;
     4 import java.util.concurrent.locks.ReentrantLock;
     5 
     6 /**
     7  * lock的使用
     8  */
     9 public class LockDemo {
    10 
    11     public static void main(String[] args) {
    12         final Outputer outputer = new Outputer();
    13         for(int index=0;index<10;index++){
    14             final int loop = index;
    15             new Thread(new Runnable() {
    16                 public void run() {
    17 //                    outputer.print("chunjiangchao"+loop);
    18                     outputer.synPrint("chunjiangchao"+loop);
    19                 }
    20             }).start();
    21             
    22         }
    23     }
    24     private static class Outputer{
    25         private Lock lock = new ReentrantLock();
    26         public void print(String name){
    27             int length = name.length();
    28             lock.lock();
    29             try {
    30                 for(int i=0;i<length;i++){
    31                     Thread.sleep(100);
    32                     System.out.print(name.charAt(i)+" ");
    33                 }
    34                 System.out.println();
    35             } catch (Exception e) {
    36                 e.printStackTrace();
    37             }finally{
    38                 lock.unlock();
    39             }
    40             
    41         }
    42         /**
    43          * 同步代码块的作用,和上面添加Lock锁的作用相同,只不过锁的对象不一样而已
    44          * @param name
    45          */
    46         public synchronized void synPrint(String name){
    47             int length = name.length();
    48             try {
    49                 for(int i=0;i<length;i++){
    50                     Thread.sleep(100);
    51                     System.out.print(name.charAt(i)+" ");
    52                 }
    53                 System.out.println();
    54             } catch (Exception e) {
    55                 e.printStackTrace();
    56             }
    57         }
    58     }
    59 
    60 }

    5.java5读写锁技术的妙用(ReadWriteLock)

    Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。Lock lock= new ReentrantLock( )

    ReadWriteLock  rwl = new ReentrantReadWriteLock( )

    读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!

    示例代码(读锁与读锁并发,写锁与写锁并发,读锁与写锁互斥

     1 package com.chunjiangchao.thread;
     2 
     3 import java.util.Random;
     4 import java.util.concurrent.locks.ReadWriteLock;
     5 import java.util.concurrent.locks.ReentrantReadWriteLock;
     6 /**
     7  * 使用读写锁 查看打印结果发现读锁与读锁之间并发,写锁与写锁间并发,读与写之间是互斥的
     8  * @author chunjaingchao
     9  *
    10  */
    11 public class ReadWriteLockDemo {
    12     public static void main(String[] args) {
    13         final Queue q3 = new Queue();
    14         for(int i=0;i<3;i++)
    15         {
    16             new Thread(){
    17                 public void run(){
    18                     while(true){
    19                         q3.get();                        
    20                     }
    21                 }
    22             }.start();
    23             new Thread(){
    24                 public void run(){
    25                     while(true){
    26                         q3.put(new Random().nextInt(10000));
    27                     }
    28                 }            
    29             }.start();
    30         }
    31     }
    32     static class Queue{
    33         private Integer integer = null;//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。
    34             ReadWriteLock rwl = new ReentrantReadWriteLock();
    35             public void get(){
    36                 rwl.readLock().lock();
    37                 try {
    38                     System.out.println(Thread.currentThread().getName() + "*****读取******");
    39                     Thread.sleep(200);
    40                     System.out.println(Thread.currentThread().getName() + "******读取*****" + integer);    
    41                 } catch (InterruptedException e) {
    42                     e.printStackTrace();
    43                 }finally{
    44                     rwl.readLock().unlock();
    45                 }
    46             }
    47             public void put(Integer data){
    48                 rwl.writeLock().lock();
    49                 try {
    50                     System.out.println(Thread.currentThread().getName() + "######写数据#######");    
    51                     Thread.sleep(200);
    52                     this.integer = data;        
    53                     System.out.println(Thread.currentThread().getName() + "#######写数据#######" + data);    
    54                 } catch (InterruptedException e) {
    55                     e.printStackTrace();
    56                 }finally{
    57                     rwl.writeLock().unlock();
    58                 }
    59             }
    60         }
    61 }

    在线程操作某个方法,执行这个方法的时候。

    自己挂写锁,然后自己挂读锁也是可以的(因为这是在当前线程同一个方法中的)。自己挂写锁,是为了防止其他人进入程序进行写的操作。但是,不应该进制自己进入。(在Hibernate中,锁分为读锁、写锁、更新锁)

    在JDKAPI中有相关的实例代码如下

     1 class CachedData {
     2    Object data;
     3    volatile boolean cacheValid;
     4    ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
     5    void processCachedData() {
     6      rwl.readLock().lock();//添加读锁
     7      if (!cacheValid) {
     8         // Must release read lock before acquiring write lock
     9         rwl.readLock().unlock();//如果没有数据,将读锁释放
    10         rwl.writeLock().lock();//添加写锁
    11         // Recheck state because another thread might have acquired
    12         //   write lock and changed state before we did.
    13         if (!cacheValid) {
    14           data = ...
    15           cacheValid = true;
    16         }
    17         // Downgrade by acquiring read lock before releasing write lock
    18         rwl.readLock().lock();//添加读锁
    19         rwl.writeLock().unlock(); // Unlock write, still hold read//释放写锁
    20      }
    21      use(data);
    22      rwl.readLock().unlock();//释放读锁
    23    }
    24  }

    问题:设计缓存系统

    缓存系统的概念:你要找数据不要直接去找数据库,可以直接找我。 我如果没有,查找数据库给你。与你直接查找是一样的。好处就是下一次你再来的时候,我就不用操作数据库了。我直接给你。

    6.java5条件阻塞Condition的应用

    在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。(记住:每次在等待的时候,都要将判断放在while循环中,防止伪唤醒出现

    一个锁内部可以有多个Condition,即有多路等待和通知,可以参看jdk1.5提供的Lock与Condition实现的可阻塞队列的应用案例,从中除了要体味算法,还要体味面向对象的封装。在传统的线程机制中一个监视器对象上只能有一路等待和通知,要想实现多路等待和通知,必须嵌套使用多个同步监视器对象。(如果只用一个Condition,两个放的都在等,一旦一个放的进去了,那么它通知可能会导致另一个放接着往下走。)

    问题:此处为什么要创建两个Condition对象?只创建一个不就行了?

    答:如果本道程序只有两个线程的话,只创建一个Condition对象就行了。如果是超过4个线程。例如两个存放线程、两个读取线程。如果你只创建一个Condition对象,在signal的时候,会唤醒所有都处在等待状态的线程。而不是针对某一种类型的线程。(没有针对性)

    在API文档中有如下实例代码

     1 class BoundedBuffer {
     2    final Lock lock = new ReentrantLock();
     3    final Condition notFull  = lock.newCondition(); 
     4    final Condition notEmpty = lock.newCondition(); 
     5    final Object[] items = new Object[100];
     6    int putptr, takeptr, count;
     7    public void put(Object x) throws InterruptedException {
     8      lock.lock();
     9      try {
    10        while (count == items.length) 
    11          notFull.await();
    12        items[putptr] = x; 
    13        if (++putptr == items.length) putptr = 0;
    14        ++count;
    15        notEmpty.signal();
    16      } finally {
    17        lock.unlock();
    18      }
    19    }
    20    public Object take() throws InterruptedException {
    21      lock.lock();
    22      try {
    23        while (count == 0) 
    24          notEmpty.await();
    25        Object x = items[takeptr]; 
    26        if (++takeptr == items.length) takeptr = 0;
    27        --count;
    28        notFull.signal();
    29        return x;
    30      } finally {
    31        lock.unlock();
    32      }
    33    } 
    34  }

    利用BoundedBuffer写一个简单是生产者消费者模式

     1 public class BoundedBufferDemo {
     2 
     3     public static void main(String[] args) {
     4         final BoundedBuffer boundedBuffer = new BoundedBuffer();
     5         new Thread(new Runnable(){
     6 
     7             @Override
     8             public void run() {
     9                 while(true){
    10                     try {
    11                         Thread.sleep(1000);
    12                         int nextInt = new Random().nextInt();
    13                         System.out.println(new Date().toLocaleString()+"存放数据"+nextInt);
    14                         boundedBuffer.put(nextInt);
    15                     } catch (InterruptedException e) {
    16                         e.printStackTrace();
    17                     }
    18                 }
    19             }
    20             
    21         }).start();
    22         new Thread(new Runnable(){
    23             
    24             @Override
    25             public void run() {
    26                 while(true){
    27                     try {
    28                         Thread.sleep(new Random().nextInt(1000));
    29                         System.out.println(new Date().toLocaleString()+"获取数据"+boundedBuffer.take());
    30                     } catch (InterruptedException e) {
    31                         e.printStackTrace();
    32                     }
    33                 }
    34             }
    35             
    36         }).start();
    37     }
    38 }
    View Code

    问题:子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100,如此循环50次,请写出程序。

     1 package com.chunjiangchao.thread;
     2 
     3 import java.util.concurrent.locks.Condition;
     4 import java.util.concurrent.locks.Lock;
     5 import java.util.concurrent.locks.ReentrantLock;
     6 
     7 /**
     8  * 类似于生产者消费者
     9  * 子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100,如此循环50次,请写出程序。
    10  * @author chunjiangchao
    11  */
    12 public class ConditionDemo {
    13 
    14     public static void main(String[] args) {
    15         final Business business = new Business();
    16         new Thread(new Runnable() {
    17             public void run() {
    18                 for(int i = 0;i<10;i++){
    19                     business.sub(i);
    20                 }
    21             }
    22         }).start();
    23         new Thread(new Runnable() {
    24             public void run() {
    25                 for(int i = 0;i<10;i++){
    26                     business.main(i);
    27                 }
    28             }
    29         }).start();
    30     }
    31     private static class Business{
    32         private Lock lock = new ReentrantLock();
    33         private Condition condition = lock.newCondition();
    34         private boolean bShouldSub = true;
    35         public void main(int loop){
    36             lock.lock();
    37             while(bShouldSub){
    38                 try {
    39                     condition.await();
    40                 } catch (InterruptedException e) {
    41                     e.printStackTrace();
    42                 }
    43             }
    44             for(int i=0;i<100;i++){
    45                 try {
    46                     Thread.sleep(10);
    47                 } catch (InterruptedException e) {
    48                     e.printStackTrace();
    49                 }
    50                 System.out.println(loop+"……main……"+i);
    51             }
    52             bShouldSub = true;
    53             condition.signal();
    54             lock.unlock();//应该写在finally代码块里面
    55         }
    56         public void sub(int loop){
    57             lock.lock();
    58             while(!bShouldSub){
    59                 try {
    60                     condition.await();
    61                 } catch (InterruptedException e) {
    62                     e.printStackTrace();
    63                 }
    64             }
    65             for(int i=0;i<10;i++){
    66                 try {
    67                     Thread.sleep(10);
    68                 } catch (InterruptedException e) {
    69                     e.printStackTrace();
    70                 }
    71                 System.out.println(loop+"……sub……"+i);
    72             }
    73             bShouldSub = false;
    74             condition.signal();
    75             lock.unlock();//应该写在finally代码块里
    76         }
    77         
    78     }
    79 }

    问题:怎样实现3个线程的交互通信?

      1 package com.chunjiangchao.thread;
      2 
      3 import java.util.concurrent.locks.Condition;
      4 import java.util.concurrent.locks.Lock;
      5 import java.util.concurrent.locks.ReentrantLock;
      6 
      7 /**
      8  * 多线程之间的通信
      9  * 三个线程交互执行 A-B-C-A-B-C
     10  * @author chunjiangchao
     11  *
     12  */
     13 public class ThreeConditionDemo {
     14 
     15     public static void main(String[] args) {
     16         final Business business = new Business();
     17         new Thread(new Runnable() {
     18             public void run() {
     19                 for(int i = 0;i<10;i++){
     20                     business.one(i);
     21                 }
     22             }
     23         }).start();
     24         new Thread(new Runnable() {
     25             public void run() {
     26                 for(int i = 0;i<10;i++){
     27                     business.two(i);
     28                 }
     29             }
     30         }).start();
     31         new Thread(new Runnable() {
     32             public void run() {
     33                 for(int i = 0;i<10;i++){
     34                     business.three(i);
     35                 }
     36             }
     37         }).start();
     38     }
     39     private static class Business{
     40         private Lock lock = new ReentrantLock();
     41         private Condition condition1 = lock.newCondition();
     42         private Condition condition2 = lock.newCondition();
     43         private Condition condition3 = lock.newCondition();
     44         private int whichOne = 1;
     45         public void one(int loop){
     46             try {
     47                 lock.lock();
     48                 while(whichOne!=1){
     49                     condition1.await();
     50                 }
     51                 for(int i=0;i<10;i++){
     52                     Thread.sleep(10);
     53                     System.out.println("one "+loop+" 当前执行 "+i);
     54                 }
     55                 whichOne = 2;
     56                 condition2.signal();
     57             } catch (Exception e) {
     58                 e.printStackTrace();
     59             }finally{
     60                 lock.unlock();
     61             }
     62         }
     63         public void two(int loop){
     64             try {
     65                 lock.lock();
     66                 while(whichOne!=2){
     67                     condition2.await();
     68                 }
     69                 for(int i=0;i<10;i++){
     70                     Thread.sleep(10);
     71                     System.out.println("two "+loop+" 当前执行 "+i);
     72                 }
     73                 whichOne = 3;
     74                 condition3.signal();
     75             } catch (Exception e) {
     76                 e.printStackTrace();
     77             }finally{
     78                 lock.unlock();
     79             }
     80         }
     81         public void three(int loop){
     82             try {
     83                 lock.lock();
     84                 while(whichOne!=3){
     85                     condition3.await();
     86                 }
     87                 for(int i=0;i<10;i++){
     88                     Thread.sleep(10);
     89                     System.out.println("three "+loop+" 当前执行 "+i);
     90                 }
     91                 whichOne = 1;
     92                 condition1.signal();
     93             } catch (Exception e) {
     94                 e.printStackTrace();
     95             }finally{
     96                 lock.unlock();
     97             }
     98         }
     99     }
    100 
    101 }

    未完待续……

  • 相关阅读:
    SqlBulkCopy实现大容量数据快速插入数据库中
    防查询语句注入漏洞攻击
    wpf中数据绑定(Datacontext)的应用
    WPF中StackPanel的使用方法
    可空类型的使用《二》
    关于可空数据类型的介绍
    C# 之泛型详解
    C# 之String以及浅拷贝与深拷贝
    C# 小软件部分(二)
    MVC 之HTML辅助方法
  • 原文地址:https://www.cnblogs.com/chun-jiang-chao-de-gu-shi/p/5403923.html
Copyright © 2020-2023  润新知