`
hongliangpan
  • 浏览: 313243 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

EffectiveJava2第67条:避免过多同步

 
阅读更多

第67条:避免过多同步

66条说了不同步的危险,本条讲的是过度同步的问题。过度同步,可能性能降低、死锁、或不确定的行为。

并发程序第一步要保证正确,第二步才是性能。

不能在同步区域内调用外部方法,缩小同步的代码范围。 

反面教材

集合Iterator时不能 remove元素否则会产生并发修改异常

  ConcurrentModificationException

// More complex test of ObservableSet - Page 267

import java.util.*;

public class Test2 {
    public static void main(String[] args) {
        ObservableSet<Integer> set =
            new ObservableSet<Integer>(new HashSet<Integer>());

        set.addObserver(new SetObserver<Integer>() {
            public void added(ObservableSet<Integer> s, Integer e) {
                System.out.println(e);
                if (e == 23) s.removeObserver(this);
            }
        });

        for (int i = 0; i < 100; i++)
            set.add(i);
    }
}

死锁例子

// Perverse test of ObservableSet - bottom of Page 267

import java.util.*;
import java.util.concurrent.*;

public class Test3 {
    public static void main(String[] args) {
        ObservableSet<Integer> set =
            new ObservableSet<Integer>(new HashSet<Integer>());

        // Observer that uses a background thread needlessly
        set.addObserver(new SetObserver<Integer>() {
            public void added(final ObservableSet<Integer> s, Integer e) {
                System.out.println(e);
                if (e == 23) {
                    ExecutorService executor =
                        Executors.newSingleThreadExecutor();
                    final SetObserver<Integer> observer = this;
                    try {
                        executor.submit(new Runnable() {
                            public void run() {
                                s.removeObserver(observer);
                            }
                        }).get();
                    } catch (ExecutionException ex) {
                        throw new AssertionError(ex.getCause());
                    } catch (InterruptedException ex) {
                        throw new AssertionError(ex.getCause());
                    } finally {
                        executor.shutdown();
                    }
                }
            }
        });

        for (int i = 0; i < 100; i++)
            set.add(i);
    }
}
// Broken - invokes alien method from synchronized block! - Page 265

import java.util.*;
import java.util.concurrent.*;

public class ObservableSet<E> extends ForwardingSet<E> {
    public ObservableSet(Set<E> set) { super(set); }

    private final List<SetObserver<E>> observers =
        new ArrayList<SetObserver<E>>();

    public void addObserver(SetObserver<E> observer) {
        synchronized(observers) {
            observers.add(observer);
        }
    }

    public boolean removeObserver(SetObserver<E> observer) {
        synchronized(observers) {
            return observers.remove(observer);
        }
    }

    // This method is the culprit
    private void notifyElementAdded(E element) {
        synchronized(observers) {
            for (SetObserver<E> observer : observers)
                observer.added(this, element);
        }
    }

    // Alien method moved outside of synchronized block - open calls - Page 268
//  private void notifyElementAdded(E element) {
//      List<SetObserver<E>> snapshot = null;
//      synchronized(observers) {
//          snapshot = new ArrayList<SetObserver<E>>(observers);
//      }
//      for (SetObserver<E> observer : snapshot)
//          observer.added(this, element);
//  }


    //  Thread-safe observable set with CopyOnWriteArrayList - Page 269
//
//  private final List<SetObserver<E>> observers =
//      new CopyOnWriteArrayList<SetObserver<E>>();
//
//  public void addObserver(SetObserver<E> observer) {
//      observers.add(observer);
//  }
//  public boolean removeObserver(SetObserver<E> observer) {
//      return observers.remove(observer);
//  }
//  private void notifyElementAdded(E element) {
//      for (SetObserver<E> observer : observers)
//          observer.added(this, element);
//  }

    @Override public boolean add(E element) {
        boolean added = super.add(element);
        if (added)
            notifyElementAdded(element);
        return added;
    }

    @Override public boolean addAll(Collection<? extends E> c) {
        boolean result = false;
        for (E element : c)
            result |= add(element);  // calls notifyElementAdded
        return result;
    }
}

集合添加对象 是同步方法,添加时又做其他删除的同步操作产生死锁。

删除对象的线程,想要获得对象锁,但是此对象已经被主线程锁了,所以产生死锁。

死锁最简单的例子

//代码一
class Deadlocker {
 int field_1;
 private Object lock_1 = new int[1];
 int field_2;
 private Object lock_2 = new int[1];

 public void method1(int value) {
  “synchronized” (lock_1) {
   “synchronized” (lock_2) {
    field_1 = 0; field_2 = 0;
   }
  }
 }

 public void method2(int value) {
  “synchronized” (lock_2) {
   “synchronized” (lock_1) {
    field_1 = 0; field_2 = 0;
   }
  }
 }
}



  参考代码一,考虑下面的过程:

  ◆ 一个线程(ThreadA)调用method1()。

  ◆ ThreadA在lock_1上同步,但允许被抢先执行。

  ◆ 另一个线程(ThreadB)开始执行。

  ◆ ThreadB调用method2()。

  ◆ ThreadB获得lock_2,继续执行,企图获得lock_1。但ThreadB不能获得lock_1,因为ThreadA占有lock_1。

  ◆ 现在,ThreadB阻塞,因为它在等待ThreadA释放lock_1。

  ◆ 现在轮到ThreadA继续执行。ThreadA试图获得lock_2,但不能成功,因为lock_2已经被ThreadB占有了。

  ◆ ThreadA和ThreadB都被阻塞,程序死锁。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics