同步容器

1.简述

  同步容器可以简单的理解为通过synchronized来实现同步的容器(因为使用了synchronized关键字所以在性能方面没有线程不安全的容器好),如果有多个线程调用同步容器的方法,它们将会串行执行。包括Vector和Hashtable,以及由同步容器封装类。Collections.synchronizedXxx等工厂方法创建的类。

  在Java中,同步容器主要包括2类

  • Vector、Stack、HashTable类。
  • Collections类中提供的静态工厂方法创建的类(例如Collections.synchronizedList(new ArrayList<String>()))。

2.同步容器的存在的并发问题

  单独使用同步容器所提供的方法操作,不会带来任何的并发问题。因为同步逻辑已经被封装在该操作对应的方法中。但如果使用复合操作,很有可能带来并发问题,复合操作的同步逻辑需要你自己去实现。常见的复合操作有:迭代(容器遍历)、跳转(根据指定顺序找到当前元素的下一个元素)和检查执行(先检查某一条件,该条件满足了再执行指定操作。如:若没有则添加)。Java 中,对同步容器的复合操作可能会产生异常,最常见的异常有:ArrayIndexOutOfBoundsException和ConcurrentModificationException。  

(1)ArrayIndexOutBoundsException异常

  ArrayIndexOutOfBoundsException异常代码如下

public class Test{
    public static void main(String[] args) {
        //添加 10000 个元素到容器
        for(int i = 0; i < 10000; i++)
            vector.add(i);

        //启动 N个线程执行删除操作
        for(int i = 0; i < 200; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    deleteLast(vector);//删除最后一个元素
                }
            }).start();
        }
        System.out.println("end");
    }
    
    //创建一个 Vector
    static Vector<Integer> vector = new Vector<Integer>();
    //定义删除最后一个元素的方法
    public static void deleteLast(Vector<Integer> list) {
        int lastindex = list.size() - 1;
        list.remove(lastindex);
    }
}
View Code

  执行结果是抛出异常java.lang.ArrayIndexOutOfBoundsException(数组下标越界)。在这个多线程程序中,Vector的size是不断减小的,可能一个线程已经把元素A删除了,另一个线程再去把元素A删除一遍,但是元素A已经不存在了,所以抛出异常。这种问题可以通过客户端加锁来解决。

  ArrayIndexOutOfBoundsException异常解决代码如下

public class Test{
    public static void main(String[] args) {
        //添加 10000 个元素到容器
        for(int i = 0; i < 10000; i++)
            vector.add(i);

        //启动 N个线程执行删除操作
        for(int i = 0; i < 200; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (vector){//加入同步锁
                        deleteLast(vector);//删除最后一个元素
                    }
                }
            }).start();
        }
        System.out.println("end");
    }
    
    //创建一个 Vector
    static Vector<Integer> vector = new Vector<Integer>();
    //定义删除最后一个元素的方法
    public static void deleteLast(Vector<Integer> list) {
        int lastindex = list.size() - 1;
        list.remove(lastindex);
    }
}
View Code

  通过在调用删除方法前加入synchronized关键字,执行已经不会抛出异常了。

(2)ConcurrentModificationException异常

  ConcurrentModificationException异常代码如下

public class Test {
    public static void main(String[] args) throws Exception {
        //添加 100 个元素到容器
        for(int i = 0; i < 100; i++) {
            vector.add(i);
        }
        
        //创建一个线程,遍历 Vector
        new Thread(new Runnable() {
            @Override
            public void run() {
                Iterator<Integer> it = vector.iterator();
                while(it.hasNext()) {
                    Integer integer = (Integer) it.next(); //调用next
                    try {
                        Thread.sleep(100);    //睡眠
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        //创建一个线程,遍历 Vector
        new Thread(new Runnable() {
            @Override
            public void run() {
                //利用迭代器遍历,在遍历的同时删除一个元素
                Iterator<Integer> it = vector.iterator();
                while(it.hasNext()) {
                    Integer integer = (Integer) it.next();
                    if(integer == 5) {
                        it.remove(); //删除一个元素,更新变量 modCount,expectedModCount的值。
                    }
                }
            }
        }).start();
    }
    
    static Vector<Integer> vector = new Vector<Integer>(); //创建一个 Vector
}
View Code

  执行结果是抛出异常java.util.ConcurrentModificationException。在这个多线程程序中,Vector等同步容器进行并发迭代修改的时候,就会出现这个异常因为it.next()方法会检查这两个变量(modCount、expectedModCount)是否相等,不等则抛出这个异常。直接调用v.remove(),它会更新 modCount 的值,却没有更新 expectedModCount 的值,所以抛出异常。这种问题可以在使用iterator迭代的时候加锁来解决。

  ConcurrentModificationException异常解决代码如下

public class Test {
    public static void main(String[] args) throws Exception {
        //添加 100 个元素到容器
        for(int i = 0; i < 100; i++) {
            vector.add(i);
        }
        
        //创建一个线程,遍历 Vector
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized(vector) {
                    Iterator<Integer> it = vector.iterator();
                    while(it.hasNext()) {
                        Integer integer = (Integer) it.next(); //调用next
                        try {
                            Thread.sleep(100);    //睡眠
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            }
        }).start();
        //创建一个线程,遍历 Vector
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized(vector) {
                    //利用迭代器遍历,在遍历的同时删除一个元素
                    Iterator<Integer> it = vector.iterator();
                    while(it.hasNext()) {
                        Integer integer = (Integer) it.next();
                        if(integer == 5) {
                            it.remove(); //删除一个元素,更新变量 modCount,expectedModCount的值。
                        }
                    }
                }
            }
        }).start();
    }
    
    static Vector<Integer> vector = new Vector<Integer>(); //创建一个 Vector
}
View Code

  通过在使用iterator迭代前加入synchronized关键字,执行已经不会抛出异常了。

posted on 2020-10-30 15:34  码农记录  阅读(160)  评论(0编辑  收藏  举报

导航