两个线程交替打印1-100的多种方式

一、不使用锁,利用volatile实现

//两个线程,一个打印奇数,一个打印偶数
public class OneToHundred{
   static volatile int flag = 0;
   public static void main(String[] args){
      new Thread(new Task1(),"A").start();
      new Thread(new Task2(),"B").start();
   }
}

class Task1 implements Runnable{
   @Override
   public void run(){
     int i = -2;
     while(i<=99){
       if(OneToHundred.flag == 0){
          i+=2;
          System.out.println("a:" + i);
          OneToHundred.flag = 1;
       }
     }
   }
}

class Task2 implements Runnable{
   @Override
   public void run(){
     int i = -1;
     while(i<=98){
       if(OneToHundred.flag == 1){
          i+=2;
          System.out.println("b:" + i);
          OneToHundred.flag = 0;
       }
     }
   }
}

二、使用synchronized关键字

public class OneToHundred2 {
    public static void main(String[] args){
        Number number = new Number();
        Thread t1 = new Thread(number);
        Thread t2 = new Thread(number);
        t1.setName("线程1");
        t2.setName("线程2");
        t1.start();
        t2.start();
    }
}

class Number implements Runnable{
   private int number = 1;
   
   @Override
   public void run(){
     while(number < 100){
       synchronized(this){
         number++;
         notify();
         try{
           if(number < 100){
            wait();
           }  
         }catch (InterruptedException e) {
                    e.printStackTrace();
        }        
      }
     } 
  }
}

这个需要注意,B一开始是阻塞在lock队列里的,所以A调用wait()释放锁以后,JVM会自动调度lock队列中等待的线程去获取锁,此时只有B,B被唤醒获得锁,所以A第一次执行notify是没有任何作用的,因为notify的作用是将因为wait()进入wait_set的线程移到lock队列,然后抢锁。所以,notify是B先起作用,将A移动到lock队列,这样B调用wait后,A会被唤醒的到锁。
三、使用ReentranLock(功能实现,但main进程无法结束 + 改正)

public class OneToHundred {
    public static void main(String[] args) throws InterruptedException {
        Task t = new Task();
        Thread t1 = new Thread(t,"A");
        Thread t2 = new Thread(t,"B");
        t1.start();
        t2.start();
    }
}

class Task implements Runnable{
   private int number = 0;
   private ReentranLock lock = new ReentranLock();
   private Condition condition = lock.newCondition();
   
   @Override
   public void run(){
     while(number<100){
       lock.lock();
       number++;
       condition.signal();
       try{
          if(number<100)
             condition.await();
       }catch (InterruptedException e) {
             e.printStackTrace();
       }
     }
  }
}

//缺少了unlock()操作,因为之前,我以为一个线程调用await()后就会释放锁并阻塞,当其被唤醒后是不持有锁的,
//其实不然,ReentranLock的底层实现,线程调用await()之后,虽然会释放锁,但会进入WAITTING状态的队列,
//当被唤醒,会重新尝试持有锁,然后再继续执行。
//所以这个地方如果不使用unlock()操作的话,会导致一个线程持有很多次锁,当运行完,即number到100之后,
//打印100的那个线程跳出while循环结束,但是lock的锁被其所持有,导致另一个线程一直被阻塞在等待队列中。

//改正
class Task2 implements Runnable{
   private int number = 0;
   private ReentranLock lock = new ReentranLock();
   private Condition condition = lock.newCondition();
   
   @Override
   public void run(){
     while(number<100){
       lock.lock();
       number++;
       condition.signal();
       try{
          if(number<100)
             condition.await();
       }catch (InterruptedException e) {
             e.printStackTrace();
       }finally{
          lock.unlock(); 
       }
     }
  }
}

四、使用ReentranLock(固定一个打印奇数,一个打印偶数)

//三实现的方式,虽然也是一个打印奇数,一个打印偶数,但是A打印奇数还是偶数是随机的,即A打印奇数,则B打印偶
//如果A随机打印的是偶数,那么B打印奇数
//现在固定,A就是打印奇数,B就是打印偶数
public class OneToHundred {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread( new Task(1),"A");
        Thread t2 = new Thread( new Task(0),"B");
        t1.start();
        t2.start();
    }
}

class Task implements Runnable{
   private static int number = 0;
   private int flag = 0;
   private static ReentrantLock lock = new ReentrantLock();
   private static Condition condition = lock.newCondition();
   
   Task(int flag){
     this.flag = flag;
   }
   @Override
   public void run(){
     while(number < 100){
       lock.lock();
       if(number >=100) break;
       if(b % 2 == this.flag){
          flag++;
       }else{
          try{
            condition.await();
          }catch (InterruptedException e) {
            e.printStackTrace();
          } 
       }
       condition.signalAll();
       lock.unlock();
     }
   }
}

你可能感兴趣的:(#,java,并发,多线程,并发编程)