当前位置:首页 > 开发 > 编程语言 > Java > 正文

Java SE: Effective Java Notes "Concurrency"

发表于: 2014-09-12   作者:DavyJones2010   来源:转载   浏览次数:
摘要: Use the Advanced Concurrent Tools instead of the legacy wait() and notify(). Java Advanced Concurrent Tools:     1> Executor Framework: ExecutorService, Callable, Executors ...  

Use the Advanced Concurrent Tools instead of the legacy wait() and notify().

Java Advanced Concurrent Tools:

    1> Executor Framework: ExecutorService, Callable, Executors ...

    2> Concurrent Collection: ConcurrentMap, BlockingQueue ... (Is there any ConcurrentSet*(1) and ConcurrentList*(2) ?)

    3> Synchronizer: CountDownLatch, Semaphore, CyclicBarrier, Exchanger, Locks: (ReentrantLock, ReadLock, WriteLock)...

 

Never call an alien method from within a synchronized region.

1) Example for ConcurrentModificationException:

public class ConcurrentModificationTest {
	@Test(expected=ConcurrentModificationException.class)
	public void test() {
		Set<String> set = Sets.newHashSet("A", "B", "C");
		for (String str : set) {
			if ("B".equals(str)) {
				set.remove(str);
			}
		}
	}
}

    We cannot modify the set when iterating it.

    But how did JDK inspect this kind of issue(ConcurrentModification)?

final Entry<K,V> nextEntry() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException(); // Exception is thrown here
    Entry<K,V> e = next;
    if (e == null)
        throw new NoSuchElementException();

    if ((next = e.next) == null) {
        Entry[] t = table;
        while (index < t.length && (next = t[index++]) == null)
           ;
    }
    current = e;
    return e;
}

    We can know that this exception is not thrown when we execute set.remove(str), it is thrown when we iterate the element just after we executing remove(str);

    Thus we can run across this exception by just jump out of this for-loop. But this shortcut is limited(can only remove one element for the whole loop) and deprecated(we can use a new filteredSet to store the filtered elements instead).

@Test
public void test() {
	Set<String> set = Sets.newHashSet("A", "B", "C");
	for (String str : set) {
		if ("B".equals(str)) {
			set.remove(str);
			break; // Here we just jump out of the loop, thus do not have a chance to execute nextEntry() and check modCount == expectedModCount
		}
	}
	assertEquals(2, set.size());
}

 

2. ObservableSet Example -"Effective Java 2nd Edition, Chapter 10"

public class ForwardingSet<E> implements Set<E> {
	private final Set<E> s;
	public ForwardingSet(Set<E> s) {
		this.s = s;
	}

	public void clear() {
		s.clear();
	}
	public boolean contains(Object o) {
		return s.contains(o);
	}
	public boolean isEmpty() {
		return s.isEmpty();
	}
	public int size() {
		return s.size();
	}
	public Iterator<E> iterator() {
		return s.iterator();
	}
	public boolean add(E e) {
		return s.add(e);
	}
	public boolean remove(Object o) {
		return s.remove(o);
	}
	public boolean containsAll(Collection<?> c) {
		return s.containsAll(c);
	}
	public boolean addAll(Collection<? extends E> c) {
		return s.addAll(c);
	}
	public boolean removeAll(Collection<?> c) {
		return s.removeAll(c);
	}
	public boolean retainAll(Collection<?> c) {
		return s.retainAll(c);
	}
	public Object[] toArray() {
		return s.toArray();
	}
	public <T> T[] toArray(T[] a) {
		return s.toArray(a);
	}
	@Override
	public boolean equals(Object o) {
		return s.equals(o);
	}
	@Override
	public int hashCode() {
		return s.hashCode();
	}
	@Override
	public String toString() {
		return s.toString();
	}
}
public class ObservableSet<E> extends ForwardingSet<E> {
	private final List<SetObserver<E>> observers = new ArrayList<SetObserver<E>>();

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

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

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

	@Override
	public boolean add(E element) {
		boolean added = super.add(element);
		if (added) {
			synchronized (observers) {
				for (SetObserver<E> observer : observers)
					observer.elementAdded(this, element);
			}
		}
		return added;
	}
}
@Test(expected = ConcurrentModificationException.class)
public void addTest() throws InterruptedException {
	ObservableSet<Integer> set = new ObservableSet<>(new HashSet<Integer>());

	set.addObserver(new SetObserver<Integer>() {
		@Override
		public void elementAdded(final ObservableSet<Integer> observalSet,
				Integer element) {
			System.out.println(element + " added to set");
			final SetObserver<Integer> setObserverInstance = this;
			if (23 == element) {
				observalSet.removeObserver(setObserverInstance);
			}
		}
	});

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

   1) We can find that because "set.add()" need current thread to get lock for "observers", and "observalSet.removeObserver()" need current thread to get lock for "observers" too,

       And because the intrinsic lock in Java is ReentrantLock, thus when we invoke "observalSet.removeObserver()" in main thread, we don't have to get the lock again, and thus ConcurrentModificationException will be thrown.

       Just it behaves exactely the same as the first example above.

    Another version of removeObserver:

@Test
public void addTest() throws InterruptedException {
	ObservableSet<Integer> set = new ObservableSet<>(new HashSet<Integer>());

	set.addObserver(new SetObserver<Integer>() {
		@Override
		public void elementAdded(final ObservableSet<Integer> observalSet,
				Integer element) {
			System.out.println(element);
			final SetObserver<Integer> observerInstance = this;
			if (23 == element) {
				new Thread(new Runnable() {
					@Override
					public void run() {
					observalSet.removeObserver(observerInstance);
					}
				}).start();
			}
		}
	});

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

    Here we created a new thread to call "observalSet.removeObserver()", and the new thread need to get lock "observers", but the lock is currently held by the main thread, thus the removal thread need to wait until the main thread loops over and relase the lock.

    The result is that "observalSet.removeObserver()" executed only after 100 elements added to set.
    And this is esay to jump into DeatLock:

public boolean isObserverEmpty() {
	synchronized (observers) {
		return observers.isEmpty();
	}
}
@Test
public void addTest() throws InterruptedException {
	ObservableSet<Integer> set = new ObservableSet<>(new HashSet<Integer>());

	set.addObserver(new SetObserver<Integer>() {
		@Override
		public void elementAdded(final ObservableSet<Integer> observalSet,
				Integer element) {
			System.out.println(element);
			final SetObserver<Integer> observerInstance = this;
			new Thread(new Runnable() {
				@Override
				public void run() {
					try {
					observalSet.removeObserver(observerInstance);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}).start();
		}
	});
	while (!set.isObserverEmpty()) {
		Thread.sleep(10);
	}
}

    The example above wil jump into DeadLock: Main thread hold the "observers" lock(Thread.sleep will not release this lock) and wait for the listener thread to delete observer; At the same time, the listener thread trying to get the "observers" lock in order to delete the observer.

    This is a typical deatlock example. But it has the same mechanism that applies in Java GUI Toolkit which lead to exception/deadlock/starvation in practice.

    The root reason why this kind of program is awkward/dangerous, is that it has elementeAdded() method which is designed to be override by client, but this method is just inside a synchronized region.

    "Inside a synchronized region, do not invoke a method that is designed to be overriden."

    "To avoid liveness and safety failures, never cede control to the client within a synchronized method or block."

 

 

Reference Links:
1) http://stackoverflow.com/questions/6916385/is-there-a-concurrent-list-in-javas-jdk

2) http://stackoverflow.com/questions/6992608/why-there-is-no-concurrenthashset-against-concurrenthashmap

 

Java SE: Effective Java Notes "Concurrency"

  • 0

    开心

    开心

  • 0

    板砖

    板砖

  • 0

    感动

    感动

  • 0

    有用

    有用

  • 0

    疑问

    疑问

  • 0

    难过

    难过

  • 0

    无聊

    无聊

  • 0

    震惊

    震惊

编辑推荐
1. Risks of Threads: 1> Safety Hazards We can add annotation: @NotThreadSafe, @ThreadSafe,
Chapter 2. Thread Safety Whether an object needs to be thread-safe depends on whether it will
1. 简介 1.1 多线程的好处 提高性能,提高吞吐量,开发多核CPU的性能; 使UI应答更顺畅 1.2 多线程的
【转】http://www.blogjava.net/vincent/archive/2009/07/16/287055.html 关于 Java Concurrency 自
对数据同步访问封装的策略 我们经常操作一些本身不是线程安全的对象时,在多线程的环境下就会考虑到
引言 以前有几次碰到过一个有意思的多线程问题,当时的场景看起来比较简单。有两个线程,他们都需要
现在在CPU上,摩尔定律已经失效,大家都不再追求高频率,而是越来越追求多核,阿姆达尔定律似乎更重
Java API中有很多都使用了SecurityManager,这到底是什么玩意?最近看公司的产品的源码,也有不少Se
1.内置监控(happybit.jsp) 2.内置管理 3.先测量性能,再优化 4.为版本并存使用独立的jre 5.了解jdb
静态导入 l JDK 1.5 增加的静态导入语法用于导入指定类的某个静态属性值(方法)或全部静态属性值(方
版权所有 IT知识库 CopyRight © 2009-2015 IT知识库 IT610.com , All Rights Reserved. 京ICP备09083238号