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

  

}  

  



你可能感兴趣的:( Thinking in Java P689 同步问题)