多线程之 线程安全与非线程安全

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程之 线程安全与非线程安全相关的知识,希望对你有一定的参考价值。

 

  ArrayListVector有什么区别?

  HashMapHashTable有什么区别?

  StringBuilderStringBuffer有什么区别

  这些都是Java面试中常见的基础问题。面对这样的问题,回答是:ArrayList是非线程安全的,Vector是线程安全的;HashMap是非线程安全的,HashTable是线程安全的;StringBuilder是非线程安全的,StringBuffer是线程安全的。

  此时如果继续问:什么是线程安全?线程安全和非线程安全有什么区别?分别在什么情况下使用?

线程安全:

  多个线程类并发操作某类的某个方法,(在该方法内部)修改这个类的某个成员变量的值,不会出错,则我们就说,该的这个方法是线程安全的。

  某类的某方法是否线程安全的关键是:

  (1) 该方法是否修改该类的成员变量;

  (2) 是否给该方法加锁(是否用synchronized关键字修饰)。 

线程不安全:

  多个线程类并发操作某类的某个方法,(在该方法内部)修改这个类的某个成员变量的值,很容易就会发生错误,故我们就说,这个方法是线程不安全的。如果要把这个方法变成线程安全的,则用 synchronized关键字来修饰该方法即可。

   注:用 synchronized关键字修饰方法,会导致加锁,虽然可以使该方法线程安全,但是会极大的降低该方法的执行效率,故要慎用该关键字。

  技术分享

 

线程安全:

  多个线程()同时执行同一段代码,就可能出现安全问题。

  看下面的代码,来理解线程安全 

  public Double pi() {   
    int a = 22;   
    int b = 7;   
    return new Double(a / b);   
  }  

  现在在执行这个方法时,每一个线程都有自己的独立的栈区。当线程进入到方法执行断的时候,一个方法变量在方法代码段中被创建,并保存在线程的栈区(静态方法也放在这里)。不同线程执行这段代码时,会有不同的a/b变量。所以这里是线程安全的,因为没有数据共享。 

  考虑下面的例子,多线程情况下只执行一次并可以重用结果: 

  private Double pi = null;   
  public Double pi() {   
    if (pi == null) {   
      pi = new Double(22 / 7);   
    }   
    return pi;   
  }   

  这个地方虽然优化了,但可惜他不是线程安全的。 

  两个线程并发执行的时候同时进入到pi==null这个位置,这样可能会new出一个脏的数据. 

 

  Consider this example which uses ThreadLocal to make the method pi() again thread-safe while still offering performance gains:  

  private static ThreadLocal pi = new ThreadLocal();   
  public Double pi() {   
    if (pi.get() == null) {   
      pi.set(new Double(22 / 7));   
    }     
    return (Double)pi.get();   
  }  
  ThreadLocal类封装了任何类型对象,并把它绑定到当前线程。线程执行pi()方法的时候,实例pi返回的是当前线程的对象。这样的调用是线程安全的。 
  Writing thread-safe code requires you to be careful when using instance variables or static variables, especially when you are modifying objects that may be used by other threads.   

 

  用 ArrayList还是 Vector二者如何取舍? 

  线程安全:指多线程操作同一个对象的某方法,修改该类的成员变量时,不会出现错误。 

  非线程安全:指多线程操作同一个对象的某方法,修改该类的成员变量时,可能会出现错误。 

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

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

 

非线程安全!=不安全 

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

  非线程安全并不是多线程环境下就不能使用。注意上面有说到:多线程操作同一个对象。注意是同一个对象 

  比如最上面那个模拟,就是在主线程中new的一个ArrayList然后多个线程操作同一个ArrayList对象,就会有安全问题。 

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

 

总结: 

  若多个线程同时修改某个外部传来的对象的成员变量,很容易就会出现错误,我们称之为线程不安全。(该类的这个方法是线程不安全的。若要线程安全,用synchronized关键字修饰即可)

  技术分享

 

以上是关于多线程之 线程安全与非线程安全的主要内容,如果未能解决你的问题,请参考以下文章

什么是线程安全与非线程安全?

多线程安全问题之锁策略,程序员一定要掌握的东西

Java常用类--String类(StringStringBufferStringBuilder)的区别 & 线程安全与非安全

java线程安全问题之静态变量实例变量局部变量

java线程安全问题之静态变量实例变量局部变量

多线程(五):解决线程不安全方案