zoukankan      html  css  js  c++  java
  • [原创] Thinking in Java P689 同步问题

    //: concurrency/CriticalSection.java
    // Synchronizing blocks instead of entire methods. Also
    // demonstrates protection of a non-thread-safe class
    // with a thread-safe one.
    //package concurrency;
    import java.util.concurrent.*;
    import java.util.concurrent.atomic.*;
    import java.util.*;
    
    class Pair { // Not thread-safe
      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 values not equal: " + Pair.this);
        }
      }
      // Arbitrary invariant -- both variables must be equal:
      public void checkState() {
        if(x != y)
          throw new PairValuesNotEqualException();
      }
    }
    
    // Protect a Pair inside a thread-safe class:
    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() {
        // Make a copy to keep the original safe:
        return new Pair(p.getX(), p.getY());
      }
      // Assume this is a time consuming operation
      protected void store(Pair p) {
        storage.add(p);
        try {
          TimeUnit.MILLISECONDS.sleep(50);
        } catch(InterruptedException ignore) {}
      }
      public abstract void increment();
    }
    
    // Synchronize the entire method:
    class PairManager1 extends PairManager {
      public synchronized void increment() {
        p.incrementX();
        p.incrementY();
        store(getPair());
      }
    }
    
    // Use a critical section:
    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(); //此处可能没有互斥,getPair()使用的是synchronized提供的对象锁,其他锁不起作用
        }
      }
    }
    
    public class CriticalSection {
      // Test the two different approaches:
      static void
      testApproaches(PairManager pman1, PairManager pman2) {
        ExecutorService exec = Executors.newCachedThreadPool();
        PairManipulator
          pm1 = new PairManipulator(pman1),
          pm2 = new PairManipulator(pman2);
        PairChecker
          pcheck1 = new PairChecker(pman1),
          pcheck2 = new PairChecker(pman2);
        exec.execute(pm1);
        exec.execute(pm2);
        exec.execute(pcheck1);
        exec.execute(pcheck2);
        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);
      }
    } /* Output: (Sample)
    pm1: Pair: x: 15, y: 15 checkCounter = 272565
    pm2: Pair: x: 16, y: 16 checkCounter = 3956974
    *///:~
    //: concurrency/ExplicitCriticalSection.java
    // Using explicit Lock objects to create critical sections.
    //package concurrency;
    import java.util.concurrent.locks.*;
    
    // Synchronize the entire method:
    class ExplicitPairManager1 extends PairManager {
      private Lock lock = new ReentrantLock();
      public Pair getPair() { //覆盖基类方法,统一使用Lock锁
        // Make a copy to keep the original safe:
    	lock.lock();
    	try {
    	  return new Pair(p.getX(), p.getY());
    	} finally {
    		lock.unlock();
    	}
      }
      public synchronized void increment() {
        lock.lock();
        try {
          p.incrementX();
          p.incrementY();
          store(getPair());
        } finally {
          lock.unlock();
        }
      }
    }
    
    // Use a critical section:
    class ExplicitPairManager2 extends PairManager {
      private Lock lock = new ReentrantLock();
      public void increment() {
        Pair temp;
        lock.lock(); //与对象pm2的getPair()中持有锁的方式不同
        try {
          p.incrementX();
          p.incrementY();
          temp = getPair();
        } finally {
          lock.unlock();
        }
        store(temp);
      }
    }
    
    public class ExplicitCriticalSection {
      public static void main(String[] args) throws Exception {
        PairManager
          pman1 = new ExplicitPairManager1(),
          pman2 = new ExplicitPairManager2();
        CriticalSection.testApproaches(pman1, pman2);
      }
    } /* Output: (Sample)
    pm1: Pair: x: 15, y: 15 checkCounter = 174035
    pm2: Pair: x: 16, y: 16 checkCounter = 2608588
    *///:~
    明确下synchronized的几个关键点:
    
    A.无论synchronized关键字加在方法上还是对象上,他取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。 
    B.每个对象只有一个锁(lock)和之相关联。 
    C.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。 
    
    synchronized可以加在方法上,也可以加在对象上,通常理解为,只有持有了锁才可以进行对应代码块的执行。
    
    java.util.concurrent.locks包下面提供了一些锁的实现,有读写锁,公平锁等。
    
    将synchronized替换成lock的实现可以提升性能:
    
    1. 大部分应用场景是读写互斥,写和写互斥,读和读不互斥。而synchronized则是都互斥。
    
        可以利用读写锁来优化性能,读锁锁住读的代码块,写锁锁住写的代码块。
    
    2. 要确保你在理解原来利用到synchronized的代码逻辑,避免一概而论地把synchronized替换成锁。
    
    public void getPair(){   
              return "x="+x + ",y="+y;   
    }   
      
      
    //这个函数   
    public synchronized void  increment() {               
                x++;     
                y++;        
                getPair();   
                                    
    }
      
    //可以替换成   
    public void  increment() {   
      lock.lock();   
      try{   
        x++;   
        y++;   
        getPair();   
      }finally{   
       lock.unlock();   
    }   
      
    //但是,如果getPair()是synchronized    
    public synchronized void getPair(){   
              return "x="+x + ",y="+y;   
    }   
    //还能替换这个函数吗?   
    public synchronized void  increment() {               
                x++;     
                y++;               
                getPair();                         
     }     
    //这时候就不能简单地使用lock来替换了,这里要调用getPair();必需申请到对象锁,这个时候increment也要竞争这把锁   
    //因此这里的代码效果是读写互斥。   
    //如果只是用lock来锁住increment,则达不到效果。还得同时锁getPair();   
    //这里嵌套了synchronized,而synchronized的嵌套结构中在同一个对象的方法上是共享一把锁的。   
    //上面只是简单的例子,java编程思想的多线程编程中有更详细的这个例子,有兴趣的可以看看。   
      
    }  
    

      



  • 相关阅读:
    jquery.stop()停止动画
    字符串转义
    CSS规范 (命名)- 分类方法
    inline-block 间距
    jquery中offset(),position()
    PHP把数组转换为JSON字符串
    json对象
    C#-静态实例
    C#-readonly与const区别
    SQL-事务隔离级别与锁
  • 原文地址:https://www.cnblogs.com/JulyZhang/p/2182605.html
Copyright © 2011-2022 走看看