第四章 多線程中隱蔽的錯誤

1. 併發下的ArrayList

ArrayList是一個線程不安全的容器,多線程使用會導致錯誤,如下面代碼。

public class ArrayListMultiThread {
    public static ArrayList<Integer> al = new ArrayList<>();
    public static class AddThread implements Runnable {
        @Override
        public void run() {
            for(int i=0; i<100000; i++) {
                al.add(i);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new AddThread());
        Thread t2 = new Thread(new AddThread());
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(al.size());
    }
}

改進的方法只需要將ArrayList換成Vector。

2. 併發下的HashMap

HashMap也不是線程安全的,多線程併發訪問時也會遇到意想不到的錯誤,甚至是死鎖。

public class HashMapMultiThread {
    public static Map<String, String> map = new HashMap<>();

    public static class AddThread implements Runnable {
        int start = 0;
        public AddThread(int start) {
            this.start = start;
        }
        @Override
        public void run() {
            for(int i=start; i<100000; i+=2) {
                map.put(Integer.toString(i), Integer.toBinaryString(i));
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new HashMapMultiThread.AddThread(0));
        Thread t2 = new Thread(new HashMapMultiThread.AddThread(1));
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(map.size());
    }
}

改進的方法是使用ConcurrentHashMap代替HashMap。

3. 錯誤的加鎖

如下計數功能有一個隱晦的錯誤。

public class BadLockOnInteger implements Runnable{
    public static Integer i = 0;
    static BadLockOnInteger instance = new BadLockOnInteger();

    @Override
    public void run() {
        for(int j=0; j<100000; j++) {
            synchronized (i) {
                i++;
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
}

在Java中,Integer屬於不變對象,即1和2是兩個不同的Integer對象的值,底層調用了Integer.valueOf(),而這個方法是生成一個新的Integer對象,因此兩個線程加的鎖加到了不同的對象上,從而導致臨界區代碼控制出現問題。可將synchronized (i)替換成synchronized (instance)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章