第四章 多线程中隐蔽的错误

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

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