JAVA中的线程安全与非线程安全

来源:互联网 发布:怎么搞垮一个淘宝店 编辑:程序博客网 时间:2024/05/22 03:39

原文:http://blog.csdn.net/xiao__gui/article/details/8934832

ArrayList和Vector有什么区别?HashMap和HashTable有什么区别?StringBuilder和StringBuffer有什么区别?这些都是Java面试中常见的基础问题。面对这样的问题,回答是:ArrayList是非线程安全的,Vector是线程安全的;HashMap是非线程安全的,HashTable是线程安全的;StringBuilder是非线程安全的,StringBuffer是线程安全的。因为这是昨晚刚背的《Java面试题大全》上面写的。此时如果继续问:什么是线程安全?线程安全和非线程安全有什么区别?分别在什么情况下使用?这样一连串的问题,一口老血就喷出来了…

非线程安全的现象模拟

这里就使用ArrayList和Vector二者来说明。

下面的代码,在主线程中new了一个非线程安全的ArrayList,然后开1000个线程分别向这个ArrayList里面添加元素,每个线程添加100个元素,等所有线程执行完成后,这个ArrayList的size应该是多少?应该是100000个?

public class Main{    public static void main(String[] args)    {        // 进行10次测试        for(int i = 0; i < 10; i++)        {            test();        }    }    public static void test()    {        // 用来测试的List        List<Object> list = new ArrayList<Object>();        // 线程数量(1000)        int threadCount = 1000;        // 用来让主线程等待threadCount个子线程执行完毕        CountDownLatch countDownLatch = new CountDownLatch(threadCount);        // 启动threadCount个子线程        for(int i = 0; i < threadCount; i++)        {            Thread thread = new Thread(new MyThread(list, countDownLatch));            thread.start();        }        try        {            // 主线程等待所有子线程执行完成,再向下执行            countDownLatch.await();        }        catch (InterruptedException e)        {            e.printStackTrace();        }        // List的size        System.out.println(list.size());    }}class MyThread implements Runnable{    private List<Object> list;    private CountDownLatch countDownLatch;    public MyThread(List<Object> list, CountDownLatch countDownLatch)    {        this.list = list;        this.countDownLatch = countDownLatch;    }    public void run()    {        // 每个线程向List中添加100个元素        for(int i = 0; i < 100; i++)        {            list.add(new Object());        }        // 完成一个子线程        countDownLatch.countDown();    }}

上面进行了10次测试(为什么要测试10次?因为非线程安全并不是每次都会导致问题)。
输出结果:

99946

100000

100000

100000

99998

99959

100000

99975

100000

99996

上面的输出结果发现,并不是每次测试结果都是100000,有好几次测试最后ArrayList的size小于100000,甚至时不时会抛出个IndexOutOfBoundsException异常。(如果没有这个现象可以多试几次)
这就是非线程安全带来的问题了。上面的代码如果用于生产环境,就会有隐患就会有BUG了。

再用线程安全的Vector来进行测试,上面代码改变一处,test()方法中
List<Object> list = new ArrayList<Object>();
改为
List<Object> list = new Vector<Object>();
再运行程序。

输出结果:

100000

100000

100000

100000

100000

100000

100000

100000

100000

100000

再多跑几次,发现都是100000,没有任何问题。因为Vector是线程安全的,在多线程操作同一个Vector对象时,不会有任何问题。

再换成LinkedList试试,同样还会出现ArrayList类似的问题,因为LinkedList也是非线程安全的。

二者如何取舍

线线而线程安全则是多线程操作同一个对象不会有问题。

线程安全必须要使用很多synchronized关键字来同步控制,所以必然会导致性能的降低。

所以在使用的时候,如果是多个线程操作同一个对象,那么使用线程安全的Vector;否则,就使用效率更高的ArrayList。

线!=

有人在使用过程中有一个不正确的观点:我的程序是多线程的,不能使用ArrayList要使用Vector,这样才安全。

非线程安全并不是多线程环境下就不能使用。注意我上面有说到:多线程操作同一个对象。比如最上面那个模拟,就是在主线程中new的一个ArrayList然后多个线程操作同一个ArrayList对象。

如果是每个线程中new一个ArrayList,而这个ArrayList只在这一个线程中使用,那么肯定是没问题的。

线程安全的实现

线程安全是通过线程同步控制来实现的,也就是synchronized关键字。

在这里,我用代码分别实现了一个非线程安全的计数器和线程安全的计数器Counter,并对他们分别进行了多线程测试。

非线程安全的计数器:

public class Main{    public static void main(String[] args)    {        // 进行10次测试        for(int i = 0; i < 10; i++)        {            test();        }    }    public static void test()    {        // 计数器        Counter counter = new Counter();        // 线程数量(1000)        int threadCount = 1000;        // 用来让主线程等待threadCount个子线程执行完毕        CountDownLatch countDownLatch = new CountDownLatch(threadCount);        // 启动threadCount个子线程        for(int i = 0; i < threadCount; i++)        {            Thread thread = new Thread(new MyThread(counter, countDownLatch));            thread.start();        }        try        {            // 主线程等待所有子线程执行完成,再向下执行            countDownLatch.await();        }        catch (InterruptedException e)        {            e.printStackTrace();        }        // 计数器的值        System.out.println(counter.getCount());    }}class MyThread implements Runnable{    private Counter counter;    private CountDownLatch countDownLatch;    public MyThread(Counter counter, CountDownLatch countDownLatch)    {        this.counter = counter;        this.countDownLatch = countDownLatch;    }    public void run()    {        // 每个线程向Counter中进行10000次累加        for(int i = 0; i < 10000; i++)        {            counter.addCount();        }        // 完成一个子线程        countDownLatch.countDown();    }}class Counter{    private int count = 0;    public int getCount()    {        return count;    }    public void addCount()    {        count++;    }}

上面的测试代码中,开启1000个线程,每个线程对计数器进行10000次累加,最终输出结果应该是10000000。

但是上面代码中的Counter未进行同步控制,所以非线程安全。

输出结果:

9963727

9973178

9999577

9987650

9988734

9988665

9987820

9990847

9992305

9972233

稍加修改,把Counter改成线程安全的计数器:

class Counter{    private int count = 0;    public int getCount()    {        return count;    }    public synchronized void addCount()    {        count++;    }}

上面只是在addCount()方法中加上了synchronized同步控制,就成为一个线程安全的计数器了。再执行程序。

输出结果:

10000000

10000000

10000000

10000000

10000000

10000000

10000000

10000000

10000000

10000000

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 小区总有小年青骑摩托车扰民怎么办 摩托车行驶证副本丢了怎么办 摩托车驾照副本丢了怎么办 公司行驶证掉了怎么办 身份证外迁了过户的话怎么办 驾照体检报告丢了怎么办 常州医保卡丢了怎么办 驾驶证违章罚单丢了怎么办 身份证被别人办了信用卡怎么办 被别人办了信用卡怎么办 考驾照体检忘带身份证怎么办 c证扣12分怎么办新规 c照12分不够扣怎么办 扣了18分怎么办一次性 c照累计扣12分怎么办 车辆超速扣12分怎么办 一次超速扣12分怎么办 分扣了罚款未交怎么办 c照一次扣12分怎么办 人在外地身份证到期了怎么办 手机进水了屏幕不亮怎么办 北京一证通过期怎么办 小米6音量键进水怎么办 考驾照怕过不了怎么办 学车对车没感觉怎么办 居住证到期2个月怎么办 生育险差一个月怎么办 驾照扣了38分怎么办 新疆转入山东上学怎么办手续 驾照过日期换证怎么办 机动车被扣24分怎么办 车辆被扣24分怎么办 现在深圳牌十年老车怎么办? 护士证过期4年了怎么办 护士资格证延续注册过期了怎么办 护士资格证过期没注册怎么办 护士资格证注册时间过期怎么办 辅警体检视力不行怎么办 护士延续注册体检怀孕怎么办 护士资格证没有延续注册怎么办 申请信用卡没有座机号码怎么办