• 同步容器


    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关键字,执行已经不会抛出异常了。

  • 相关阅读:
    JSON 体验JSON (二)json格式化日期
    让D2006的控件面板回到D7的样式
    突破网站限制 复制网页内容
    欢迎光临
    加密Access数据库
    取得程序中一些特殊文件夹的位置
    连接带密码的Access数据库
    我被强暴,老公这样回答是人么?(转~非黄)
    【WinCE版凯立德】2012春季版地图下载
    刚刚拍到的日环食金星凌日
  • 原文地址:https://www.cnblogs.com/bl123/p/13876882.html
Copyright © 2020-2023  润新知