(34)21.3.5 临界区---Java编程思想之并发笔记

1.     临界区:只希望防止多个线程同时访问方法内部的部分代码而不是正规方法,通过这种方式分离出来的代码段被称为临界区(critical section)。使用关键字synchronized关键字建立。这里synchronized被用来指定某个对象,此对象的锁被用来对花括号内的代码进行同步。
2.     代码格式:
synchronized(object){
//同步代码块
}
这也被称为同步控制块。在进入此段代码前,必须得到synchronized对象的锁。如果其它线程已经得到了这个锁,那么就得等到锁释放以后,才能进入临界区。
3.     使用同步控制块的好处:可以使多个任务访问对象的时间性能得到显著提高。
4.     例子1:
package jiangning.c21;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class Pair{
     private int x,y;
     public Pair(int x, int y){
          this.x = x;
          this.y = y;
     }
     public Pair(){
          this(0,0);
     }
     public int getX(){
          return x;
     }
     public int getY(){
          return y;
     }
     public void incrementX(){
          x++;
     }
     public void incrementY(){
          y++;
     }
     public String toString(){
          return "x: " + x + ", y: " + y;
     }
     public class PairValuesNotEqualException extends RuntimeException{
          public PairValuesNotEqualException(){
               super("Pair value not equal:" + Pair.this);
          }
     }
     public void checkState(){
          if(x != y){
               throw new PairValuesNotEqualException();
          }
     }
}
     abstract class PairManager{
          AtomicInteger checkCounter = new AtomicInteger(0);
          protected Pair p = new Pair();
          private List<Pair> storage = Collections.synchronizedList(new ArrayList<Pair>());
          public synchronized Pair getPair(){
               return new Pair(p.getX(),p.getY());
          }
          protected void store(Pair p){
               storage.add(p);
               try {
                    TimeUnit.MILLISECONDS.sleep(50);
               } catch (InterruptedException e) {
               }
          }
          public abstract void increment();
     }
     class PairManager1 extends PairManager{
          public synchronized void increment(){
               p.incrementX();
               p.incrementY();
               store(getPair());
          }
     }
     class PairManager2 extends PairManager{
          public void increment(){
               Pair temp;
               synchronized(this){
                    p.incrementX();
                    p.incrementY();
                    temp = getPair();
               }
               store(temp);
          }
     }
     class PairManipulator implements Runnable{
          private PairManager pm;
          public PairManipulator(PairManager pm){
               this.pm = pm;
          }
          public void run(){
               while(true){
                    pm.increment();
               }
          }
          public String toString(){
               return "Pair: " + pm.getPair() +
                 " checkCounter = " + pm.checkCounter.get();
          }
     }
     class PairChecker implements Runnable{
          private PairManager pm;
          public PairChecker(PairManager pm){
               this.pm = pm;
          }
          public void run(){
               while(true){
                    pm.checkCounter.incrementAndGet();
                    pm.getPair().checkState();
               }
              
          }
     }
public class CriticalSection {
     static void testApproaches(PairManager pman1, PairManager pman2){
          ExecutorService exec = Executors.newCachedThreadPool();
          PairManipulator
            pm1 = new PairManipulator(pman1),
            pm2 = new PairManipulator(pman2);
          PairChecker
            pchecker1 = new PairChecker(pman1),
            pchecker2 = new PairChecker(pman2);
          exec.execute(pm1);
          exec.execute(pm2);
          exec.execute(pchecker1);
          exec.execute(pchecker2);
          try {
               TimeUnit.MILLISECONDS.sleep(500);
          } catch (InterruptedException e) {
               System.out.println("Sleep interrupted ");
          }
          System.out.println("pm1: " + pm1 +"\npm2: " + pm2);
          System.exit(0);
     }
     public static void main(String[] args) {
          PairManager
            pman1 = new PairManager1(),
            pman2 = new PairManager2();
          testApproaches(pman1, pman2);
     }

}


分析:
Pair不是线程安全的,因为它的约束条件需要两个变量维护成相同的值,此外自增操作不是线程安全的,并且没有任何方法标记为synchronized,所以不能保证一个Pair对象在多线程程序中不会被破坏。
解决方法:通过构建PairManager类实现这一点,PairManager类持有一个Pair对象,并控制对它的一切访问。注意唯一的public方法getPair,它是synchronized的,对抽象方法increment(),对increment()的同步控制将在实现的时候进行处理。
5. 使用同步块而不是同步整个方法的原因之一是:使得其他线程能够更多地访问资源。
6.例子用Lock实现临界区。
package  jiangning.c21;

import  java.util.concurrent.locks.Lock;
import  java.util.concurrent.locks.ReentrantLock;

class  ExplicitPairManager1  extends  PairManager{
        private  Lock  lock  =  new  ReentrantLock();
        public  synchronized  void  increment(){
              lock .lock();
              try  {
                    p .incrementX();
                    p .incrementY();
                  store(getPair());
            }  finally  {
                    lock .unlock();
            }
            
      }
}
class  ExplicitPairManager2  extends  PairManager{
        private  Lock  lock    new  ReentrantLock();
        public  void  increment() {
            Pair temp;
              lock .lock();
              try  {
                    p .incrementX();
                    p .incrementY();
                  temp = getPair();
            }  finally  {
                    lock .unlock();
            }
            store(temp);
            
      }
}
public  class  ExpliciticalSection {

        public  static  void  main(String[] args) {
            PairManager
              pman1 =  new  ExplicitPairManager1(),
              pman2 =  new  ExplicitPairManager2();
            CriticalSection. testApproaches(pman1, pman2);
      }
}

/**
pm1:  Pair:  x:  21,  y:  21  checkCounter  =  868
pm2:  Pair:  x:  22,  y:  22  checkCounter  =  1450417
*/
总结:
1)临界区实现方法有两种,一种是用synchronized,一种是用Lock显式锁实现。
2)有临界区是为了让更多的其它线程能够访问资源。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值