zoukankan      html  css  js  c++  java
  • 线程安全和非线程安全

    Java面试和笔试中经常会问到

    String线程安全StringBuffer线程安全StringBuilder非线程安全

    HashMap非线程安全的HashTable线程安全的vector线程安全的

    但是接下来会问你,不安全为什么还会用,因为HashMap效率更高,如果想让它变成安全的,加同步锁()

    那什么叫线程安全什么叫非线程安全呢?

    一、线程

    什么叫做线程呢?

    建议可以看一下阮一峰大神的博客:http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html

    讲的非常通俗易懂,留言中的内容也要看,因为是转载的,有很多地方值得商榷。

    二、线程安全和非线程安全

    当多个线程同时操作同一个对象,修改某一个属性的时候,不会发生问题就称之为“线程安全”

    可能会出现问题,就是“非线程安全的”

    比如说ArrayList和vector来说

    在主线程中,new了一个ArrayList线程,现在开100个线程,每个线程向arraylist中存放100个元素,最后ArrayList中有多少个对象,10000?

    先创建一个线程类(另外一篇文章会总结学习一下线程的三种实现方式)

     1 class MyThread implements Runnable  
     2 {  
     3     private List<Object> list;  
     4       
     5     private CountDownLatch countDownLatch;  
     6       
     7     public MyThread(List<Object> list, CountDownLatch countDownLatch)  
     8     {  
     9         this.list = list;  
    10         this.countDownLatch = countDownLatch;  
    11     }  
    12       
    13     public void run()  
    14     {  
    15         // 每个线程向List中添加100个元素  
    16         for(int i = 0; i < 100; i++)  
    17         {  
    18             list.add(new Object());  
    19         }  
    20           
    21         // 完成一个子线程  
    22         countDownLatch.countDown();  
    23     }  
    24 }  
     1 package threadTest;
     2 
     3 import java.util.ArrayList;
     4 import java.util.List;
     5 import java.util.concurrent.CountDownLatch;
     6 
     7 public class Test {     
     8         public static void test()  
     9         {  
    10             // 用来测试的List  
    11             List<Object> list = new ArrayList<Object>();  
    12               
    13             // 线程数量(1000)  
    14             int threadCount = 1000;  
    15               
    16             // 用来让主线程等待threadCount个子线程执行完毕  
    17             CountDownLatch countDownLatch = new CountDownLatch(threadCount);  
    18               
    19             // 启动threadCount个子线程  
    20             for(int i = 0; i < threadCount; i++)  
    21             {  
    22                 Thread thread = new Thread(new MyThread(list, countDownLatch));  
    23                 thread.start();  
    24             }  
    25               
    26             try  
    27             {  
    28                 // 主线程等待所有子线程执行完成,再向下执行  
    29                 countDownLatch.await();  
    30             }  
    31             catch (InterruptedException e)  
    32             {  
    33                 e.printStackTrace();  
    34             }  
    35               
    36             // List的size  
    37             System.out.println(list.size());  
    38         } 
    39          public static void main(String[] args)  
    40             {  
    41                 // 进行10次测试  
    42                 for(int i = 0; i < 10; i++)  
    43                 {  
    44                     test();  
    45                 }  
    46             }
    47 
    48 }

    最后结果:

    99799
    99814
    99765
    100000
    99868
    99830
    100000
    99915
    99858
    99830

    但是有时候会产生异常

    这就是所谓的非线程安全

    接下来换成线程安全的vector

    将test里边的ArrayList换成vector

    List<Object> list = new ArrayList<Object>();  
    List<Object> list = new Vector<Object>();  

    100000
    100000
    100000
    100000
    100000
    100000
    100000
    100000
    100000
    100000

    输出结果

    在多试几次,还是一样的结果,因为vector是线程安全的,换成LinkedList结果和ArrayList类似,因为LinkedList也是非线程安全的

    三、如何取舍

    当存在多个线程操作同一对象的时候,就采用vector等线程安全的,如果不涉及,就采用ArrayList等,因为效率高

    如果想要用ArrayList也可以,线程安全必须要使用很多synchronized关键字来同步控制,加同步锁

     List<Object> list = Collections.synchronizedList(new ArrayList<Object>());  
    synchronized关键字同步,但是这样会降低效率
    另外一种,实在方法前边用synchronized修饰方法

     一个计数器类

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

    创建线程类

     1 class MyThread implements Runnable
     2 {
     3     private Counter counter;
     4 
     5     private CountDownLatch countDownLatch;
     6 
     7     public MyThread(Counter counter, CountDownLatch countDownLatch)
     8     {
     9         this.counter = counter;
    10         this.countDownLatch = countDownLatch;
    11     }
    12 
    13     public void run()
    14     {
    15         // 每个线程向Counter中进行10000次累加
    16         for(int i = 0; i < 10000; i++)
    17         {
    18             counter.addCount();
    19         }
    20 
    21         // 完成一个子线程
    22         countDownLatch.countDown();
    23     }
    24 }

    主线程

     1 public class Main
     2 {
     3     public static void main(String[] args)
     4     {
     5         // 进行10次测试
     6         for(int i = 0; i < 10; i++)
     7         {
     8             test();
     9         }
    10     }
    11 
    12     public static void test()
    13     {
    14         // 计数器
    15         Counter counter = new Counter();
    16 
    17         // 线程数量(1000)
    18         int threadCount = 1000;
    19 
    20         // 用来让主线程等待threadCount个子线程执行完毕
    21         CountDownLatch countDownLatch = new CountDownLatch(threadCount);
    22 
    23         // 启动threadCount个子线程
    24         for(int i = 0; i < threadCount; i++)
    25         {
    26             Thread thread = new Thread(new MyThread(counter, countDownLatch));
    27             thread.start();
    28         }
    29 
    30         try
    31         {
    32             // 主线程等待所有子线程执行完成,再向下执行
    33             countDownLatch.await();
    34         }
    35         catch (InterruptedException e)
    36         {
    37             e.printStackTrace();
    38         }
    39 
    40         // 计数器的值
    41         System.out.println(counter.getCount());
    42     }
    43 }

    9760282
    9999073
    9999969
    9990000
    9998743
    9965715
    9990000
    9994370
    9990945
    9993745

    创建一个线程安全的计数器

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

    10000000
    10000000
    10000000
    10000000
    10000000
    10000000
    10000000
    10000000
    10000000

    输出结果

  • 相关阅读:
    企业信息开发平台(5)流程设计(一)
    企业信息开发平台(6)Web表单设计器开源
    Guava的常用方法示例
    apk反编译
    公司注册登记流程
    Git 使用流程
    ZIP压缩和解压字符串
    vue+elementui实现无限级动态菜单树
    vue 开发笔记
    从零到一开发博客后台管理系统(一)
  • 原文地址:https://www.cnblogs.com/Darius-Bennett/p/7858063.html
Copyright © 2011-2022 走看看