Principle

  1. To avoid liveness and safety failures, never cede control to the client within a synchronized method or block.
  2. Do as little work as possible inside synchronized regions.
  3. You should make a mutable class thread-safe (Item 70) if it is intended for concurrent use and you can achieve significantly higher concurrency by synchronizing internally than you could by locking the entire object externally. Otherwise, don't synchronize internally. Let the client synchronize externally where it is appropriate. (eg. StringBuffer vs. StringBuilder)
  4. If a method modifies a static field, you must synchronize access to this field, even if the method is typically used only by a single thread.

The failure by invoking alien method

/**

* Demo for "67 Avoid excessive synchronization".

*/

package com.effectivejava.concurrency;

import java.util.ArrayList;

import java.util.Collection;

import java.util.HashSet;

import java.util.List;

import java.util.Set;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import com.effectivejava.classinterface.ForwardingSet;

/**

* @author Kaibo Hao

*

*/

public class ObservableSet<E> extends ForwardingSet<E> {

/**

* @param s

*/

public ObservableSet(Set<E> s) {

super(s);

}

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);

}

}

private void notifyElementAdded(E element) {

synchronized (observers) {

for (SetObserver<E> observer : observers)

// calling the alien method

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;

}

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);

}

});

for (int i = 0; i < 100; i++)

set.add(i);

}

}

Case 1: Failed to remove an element from a list in the midst of iterating over it, which is illegal.

Root Cause - The iteration in the notifyElementAdded method is in a synchronized block to prevent concurrent modification, but it doesn't prevent the iterating thread itself from calling back into the observable set and modifying its observers list.

// Removing the Observer during the iteration.

set.addObserver(new SetObserver<Integer>() {

public void added(ObservableSet<Integer> s, Integer e) {

System.out.println(e);

if (e == 23) s.removeObserver(this);

}

});

Case 2: Failed to background thread for removing.

Root Cause: - The object in the synchronized region are locked by the main thread which cannot be modified by the background thread.

// Observer that uses a background thread needlessly

set.addObserver(new SetObserver<Integer>() {

@Override

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() {

@Override

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();

}

}

}

});

Reentrant lock : Locks in Java programming language are reentrant, in other words such calls above won't deadlock.

Solution 1 - Taking snapshot and move Alien method outside of synchronized block - open calls

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);

}

Solution 2(Prefered) - Thread-safe observable set with CopyOnWriteArrayList

// Thread-safe observable set with CopyOnWriteArrayList

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);

}

CopyOnWriteArrayList

It is a variant of ArrayList in which all write operations are implemented by making a fresh copy of the entire underlying array. Performance may be atrocious, but it's perfect for observer lists which are rarely modified and often traversed.

Note

In a multicore world, the real cost of excessive synchronization is not the CPU time spent obtaining locks; it is the lost opportunities for parallelism and the delays imposed by the need to ensure that every core has a consistent view of memory.

If you do synchronize your class internally, you can use various techniques to achieve high concurrency, such as lock splitting, lock striping, and nonblocking concurrency control.

Summary

To avoid deadlock and data corruption, never call an alien method from within a synchronized region. More generally, try to limit the amount of work that you do from within synchronized regions. When you are designing a mutable class, think about whether it should do its own synchronization. In the modern multicore era, it is more important than ever not to synchronize excessively. Synchronize your class internally only if there is a good reason to do so, and document your decision clearly (Item 70).

Effective Java 67 Avoid excessive synchronization的更多相关文章

  1. Effective Java 07 Avoid finallizers

    NOTE Never do anything time-critical in a finalizer. Never depend on a finalizer to update critical ...

  2. Effective Java 50 Avoid strings where other types are more appropriate

    Principle Strings are poor substitutes for other value types. Such as int, float or BigInteger. Stri ...

  3. Effective Java 59 Avoid unnecessary use of checked exceptions

    The burden is justified if the exceptional condition cannot be prevented by proper use of the API an ...

  4. Effective Java 73 Avoid thread groups

    Thread groups were originally envisioned as a mechanism for isolating applets for security purposes. ...

  5. Effective Java 05 Avoid creating unnecessary objects

    String s = new String("stringette"); // Don't do this. This will create an object each tim ...

  6. Effective Java 48 Avoid float and double if exact answers are required

    Reason The float and double types are particularly ill-suited for monetary calculations because it i ...

  7. Effective Java Index

    Hi guys, I am happy to tell you that I am moving to the open source world. And Java is the 1st langu ...

  8. 《Effective Java》读书笔记 - 10.并发

    Chapter 10 Concurrency Item 66: Synchronize access to shared mutable data synchronized这个关键字不仅保证了同步,还 ...

  9. Effective Java 目录

    <Effective Java>目录摘抄. 我知道这看起来很糟糕.当下,自己缺少实际操作,只能暂时摘抄下目录.随着,实践的增多,慢慢填充更多的示例. Chapter 2 Creating ...

随机推荐

  1. 登陆mysql时提示异常的解决方法

    [root@host2 ~]# mysql -uroot -p Enter password: ERROR (HY000): Can't connect to local MySQL server t ...

  2. 2014 Asia AnShan Regional Contest --- HDU 5078 Osu!

    Osu! Problem's Link:   http://acm.hdu.edu.cn/showproblem.php?pid=5078 Mean: 略. analyse: 签到题,直接扫一遍就得答 ...

  3. petapoco-SQLServer模型增加注释

    petapoco是个基于T4模板的轻量级ORM,好用效率高,具体介绍略了 获取注释基本原理是调用数据库::fn_listextendedproperty函数,获取扩展属性MS_Description ...

  4. C#中ListView的简单使用方法

    ListView是用于显示数据的,先在窗体中拉一个lisview控件,还有一些新增.修改.删除.查询按钮和文本框,控件名称为listview,按钮为btnInsert,btnUpate,btnDele ...

  5. [Eclipse] - 解决导入flask模块出现的Unresolved Import flask问题

    http://www.cnblogs.com/mizhon/p/4242073.html [Eclipse] - 解决导入flask模块出现的Unresolved Import flask问题 最近想 ...

  6. [Architecture Pattern] Factory Builder

    [Architecture Pattern] Factory Builder 目的 同时提供延迟注入对象.挂载注入项目这两个功能 情景 在开发系统时,如果需要在运行时间才生成并注入对象,可以套用Fac ...

  7. Azure SQL Database 时间点还原(Point in Time Restore)功能

      微软中国TechNet 7 Oct 2014 9:17 PM Comments 0 Likes 原文地址:http://blogs.technet.com/b/azuretw/archive/20 ...

  8. C#知识点总结【1】

    值类型和引用类型 从概念上看,其区别是值类型直接存储其值,引用类型存储值的引用. 在内存当中的状态,值类型存储在堆栈(zhan)中,而引用类型存储在托管堆上. ; int j = i; 上面的例子中 ...

  9. 用QQ号登陆Sharepoint,研究到最后关头卡住了。大家发力呀

    此项目未完成,登陆不了SharePoint,大家研究吧,折腾吧..... 已经完成的部分有:已经可以获取到腾讯用户信息,如: Get Access Token===============access ...

  10. android 永不关闭toast

    Toast信息提示框之所以在显示一定时间后会自动关闭,是因为在系统中有一个Toast队列;那么有些时候需要这个Toast信息提示框长时间显示,直到需要关闭它时通过代码来控制,而不是让系统自动来关闭To ...