5基礎構建模塊——同步容器類

同步容器類java

同步容器類包括Vector和Hashtable,以及同步的封裝容器類。它們實現線程安全類的方式是:將它們的狀態封裝起來,並對每一個公有方法都進行同步,使得每次只有一個線程訪問容器的狀態。數組

同步容器類的問題安全

容器上常見的複合操做:迭代、跳轉(根據指定元素找到當前元素的下一個元素)以及條件運算("若沒有則添加")。這些都是線程安全的,可是其餘線程併發的修改容器時,它們可能就會出現意料以外的行爲。併發

 

import java.util.Vector;
import java.util.concurrent.TimeUnit;

public class UnsafeVectorHelpers {
	public static Object getLast(Vector list) throws InterruptedException {
		int lastIndex = list.size() - 1;
		TimeUnit.NANOSECONDS.sleep(1);//使得線程睡眠1毫秒,爲了可以獲得想要的結果——報異常
		System.out.println("getLast:" + lastIndex);
		return list.get(lastIndex);
	}

	public static void deleteLast(Vector list) {
		int lastIndex = list.size() - 1;
		System.out.println("deleteLast:" + lastIndex);
		list.remove(lastIndex);
	}
	
	public static void main(String[] args) {
		final Vector<String> vector = new Vector<String>();
		for (int i = 0; i < 10; i++) {
			vector.add("value" +(i + 1));
		}
		
		new Thread(new Runnable() {//線程A
			@Override
			public void run() {
				try {
					UnsafeVectorHelpers.getLast(vector);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}).start();
		
		new Thread(new Runnable() {//線程B
@Override public void run() { UnsafeVectorHelpers.deleteLast(vector); } }).start(); } } /* 運行結果輸出: deleteLast:9 getLast:9 Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 9 at java.util.Vector.get(Unknown Source) at net.jcip.examples.buildingblock.UnsafeVectorHelpers.getLast(UnsafeVectorHelpers.java:11) at net.jcip.examples.buildingblock.UnsafeVectorHelpers$1.run(UnsafeVectorHelpers.java:30) at java.lang.Thread.run(Unknown Source) */

報異常了,爲何會出現這種狀況呢?A線程在得到lastIndex的值(從輸出的信息能夠得出是9)的時候睡眠了,此時線程B執行deleteLast方法輸出:deleteLast:9,而且刪除了最後一個元素,那麼此時的集合的容量大小就變成了9。A線程睡眠結束繼續執行,lastIndex的值仍是9,因此就形成了數組小標越界的異常。能夠看出在併發訪問線程安全集合類的時候有可能仍是會出現問題。ide

 

爲了不上面的異常發生,可使用客戶端加鎖來進行Vector的複合操做。函數

 

public class SafeVectorHelpers {
    public static Object getLast(Vector list) {
        synchronized (list) {
            int lastIndex = list.size() - 1;
            return list.get(lastIndex);
        }
    }

    public static void deleteLast(Vector list) {
        synchronized (list) {
            int lastIndex = list.size() - 1;
            list.remove(lastIndex);
        }
    }
}

使用了客戶端加鎖使得併發線程訪問任意時刻都只有一個線程得到鎖對Vector集合進行復核操做,這樣就避免了上述問題發生。ui

 

迭代器與ConcurrentModificationExceptionspa

容器的迭代如今也仍是複合操做,若是在迭代期間同時有線程併發的修改這個容器,那麼就會拋出一個ConcurrentModificationException異常。若是爲了避免發生這個異常能夠在迭代的時候加鎖來避免。固然想要不加鎖,那麼就能夠拷貝一個容器的副本對這個副本進行迭代(拷貝的時候也要加鎖)。線程

隱藏迭代器
必需要記住全部對共享容器的進行迭代的地方都須要加鎖。容器的toString()方法、hashCode和equals方法也會間接的執行迭代操做,當容器做爲另一個容器的元素或鍵值時,就會出現這種狀況。一樣,containsAll、removeAll和retainAll等方法,以及把容器做爲參數的構造函數,都會對容器進行迭代。全部這些間接迭代均可能拋出ConcurrentModificationException異常。code

相關文章
相關標籤/搜索