zoukankan      html  css  js  c++  java
  • java多线程实现线程同步

      由于Java支持多线程,具有并发功能,从而大大提高了计算机的处理能力。在各线程之间不存在共享资源的情况下,几个线程的执行顺序可以是随机的。但是,当两个或两个以上的线程需要共享同一资源时,线程之间的执行次序就需要协调,并且在某个线程占用这一资源时,其他线程只能等待。例如生产者和消费者的问题,只有当生产者生产出产品并将其放入货架时,消费者才能从货架上取走产品进行消费。当生产者没有生产出产品时消费者是没法消费的。同理,当生产者生产的产品堆满货架时应该暂停生产等待消费者消费。在程序设计中,可用两个线程分别代表这里的生产者和消费者,可将货架视为任一时刻只允许一个线程访问的临界资源。
      在这个问题中,两个线程要共享货架这一临界资源,需要在某些时刻(货空/货满)协调它们的工作,即货空时消费者应等待,而货满时生产者应等待。为了不致于发生混乱,还可进一步规定:当生产者往货架上放货物时不允许消费者取货物,当消费者从货架上取货物时不允许生产者放货物。这种机制在操作系统中称为线程间的同步。在同步机制中,将那些访问临界资源的程序段称为临界区。
      在Java系统中,临界区程序段是用关键字“synchronized”来标注,并通过一个称为监控器的系统软件来管理的。当执行被冠以synchronized的程序段即临界区程序时,监控器将这段程序(访问的临界资源)加锁,此时,称该线程占有临界资源,直到这段程序执行完,才释放锁。只有锁被释放后,其他线程才可以访问这些临界资源。用关键字synchronized定义临界区的语句形式是:
    synchronized  (expression)  statement   其中,expression代表类的名字,它是可选项;statement可以是一个方法,也可以是一个语句或一个语句块,最常见的是一个方法。下面通过一个例子来说明线程的同步问题。

     1 public class thread
     2 {
     3     public static void main(String [ ] args)
     4     {
     5         HoldInt h=new HoldInt( );  //h为监控器
     6         ProduceInt p=new ProduceInt(h);
     7         ConsumeInt c=new ConsumeInt(h);
     8         p.start( );
     9         c.start( );
    10     }
    11 }
    12 class HoldInt
    13 {
    14     private int sharedInt;
    15     private boolean writeAble=true;  //writeAble==true表示生产者线程能生产新数据
    16     public synchronized void set(int val)  //临界区程序段,也称为同步方法
    17     {
    18         while(!writeAble)
    19         {
    20         //生产者线程不能生产新数据时进入等待
    21             try {wait( );}
    22             catch(InterruptedException e){}
    23         }
    24         //生产者被唤醒后继续执行下面的语句
    25         writeAble=false;
    26         sharedInt=val;
    27         notify( );
    28     }
    29     public synchronized int get( )  //同步方法
    30     {
    31         while(writeAble)
    32         {
    33         //消费者线程不能消费数据时进入等待状态
    34             try
    35             {
    36                 wait( ); 
    37             }catch(InterruptedException e){}
    38         }
    39         //消费者被唤醒后继续执行下面的语句
    40         writeAble=true;
    41         notify( );
    42         return sharedInt;
    43     }
    44 }    
    45 //ProduceInt 是生产者线程
    46 class ProduceInt extends Thread {
    47     private HoldInt hi;
    48     public ProduceInt(HoldInt hiForm)
    49     {
    50         hi=hiForm;
    51     }
    52     public void run( )
    53     {   
    54         for(int i=1;i<=4;i++)
    55         { 
    56             hi.set(i);
    57             System.out.println("产生的新数据是: "+ i);
    58         }
    59     }
    60 }
    61 //ConsumeInt 是消费者线程
    62 class ConsumeInt extends Thread {
    63     private HoldInt hi;
    64     public ConsumeInt(HoldInt hiForm)
    65     {
    66         hi=hiForm;
    67     }
    68     public void run( )
    69     {
    70         for(int i=1;i<=4;i++)
    71         { 
    72             int val=hi.get( );
    73             System.out.println("读到的数据是: "+ val);
    74         }
    75     }
    76 }

      在这个程序中,共享数据sharedInt的方法set( )和get( )的头部的修饰符synchronized 使HoldInt的每个对象都有一把锁。当ProduceInt对象调用set( )方法时,HoldInt对象就被锁定。当set( )方法中的数据成员writeAble值为true时,set( )方法就可以向数据成员sharedInt中写入一个值,而get( )方法不能从sharedInt上读出值。如果set( )方法中的writeAble的值为false,则调用set( )方法中的wait( )方法,把调用set( )方法的ProduceInt对象放到HoldInt对象的等待队列中,并将HoldInt对象的锁打开,使该对象的其他synchronized方法可被调用。这个ProduceInt对象将一直在等待队列中等待,直到被唤醒使它进入就绪状态,等待分配CPU。当ProduceInt对象再次进入运行状态时,HoldInt对象就被隐含地锁定,而set( )方法将继续执行while循环中wait( )方法后面的语句。在本例中wait( )方法后面无其他语句,因此将进入下一次循环,判断while条件。                              ConsumeInt 对象调用get( )方法与ProduceInt对象调用set( )方法的情况类似,不再详述。
    该程序的运行结果如下:
    产生的新数据是:1
    产生的新数据是:2
    读到的数据是:1
    产生的新数据是:3
    读到的数据是:2
    产生的新数据是:4
    读到的数据是:3
    读到的数据是:4

  • 相关阅读:
    2、函数
    二者取其一(初遇)_网络流
    P1879 [USACO06NOV]玉米田Corn Fields
    P2831 愤怒的小鸟
    P2296 寻找道路
    序(不知道是什么时候的模拟题)
    P2243 电路维修
    P1273 有线电视网
    P2613 【模板】有理数取余
    P1373 小a和uim之大逃离
  • 原文地址:https://www.cnblogs.com/yimindu/p/3366998.html
Copyright © 2011-2022 走看看