• [原创] 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编程思想的多线程编程中有更详细的这个例子,有兴趣的可以看看。   
      
    }  
    

      



  • 相关阅读:
    HDU 1102 Constructing Roads
    HDU 1285 确定比赛名次。
    最小生成树 HDU 各种畅通工程的题,prim和kru的模板题
    HDU Jungle Roads 1301 最小生成树、
    并查集小结(转)
    HDU hdu 2094 产生冠军 拓扑排序 判定环
    模运算(转)
    拓扑排序(主要是确定环和加法) HDU 2647 Reward
    HDU 1372 Knight Moves 简单BFS
    用计算机模型浅析人与人之间沟通方式 (一)如何谈话
  • 原文地址:https://www.cnblogs.com/JulyZhang/p/2182605.html
Copyright © 2020-2023  润新知