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

    技术2024-12-17  12

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

    Processed: 0.038, SQL: 9