线程安全与共享资源-阿里云开发者社区

开发者社区> 行者武松> 正文

线程安全与共享资源

简介:
+关注继续查看

允许被多个线程同时执行的代码称作线程安全的代码。线程安全的代码不包含竞态条件。当多个线程同时更新共享资源时会引发竞态条件。因此,了解Java线程执行时共享了什么资源很重要。

局部变量

局部变量存储在线程自己的栈中。也就是说,局部变量永远也不会被多个线程共享。所以,基础类型的局部变量是线程安全的。下面是基础类型的局部变量的一个例子:


public void someMethod(){
  
  long threadSafeInt = 0;

  threadSafeInt++;
}

局部的对象引用

对象的局部引用和基础类型的局部变量不太一样。尽管引用本身没有被共享,但引用所指的对象并没有存储在线程的栈内。所有的对象都存在共享堆中。如果在某个方法中创建的对象不会逃逸出(译者注:即该对象不会被其它方法获得,也不会被非局部变量引用到)该方法,那么它就是线程安全的。实际上,哪怕将这个对象作为参数传给其它方法,只要别的线程获取不到这个对象,那它仍是线程安全的。下面是一个线程安全的局部引用样例:


public void someMethod(){
  
  LocalObject localObject = new LocalObject();

  localObject.callMethod();
  method2(localObject);
}

public void method2(LocalObject localObject){
  localObject.setValue("value");
}

样例中LocalObject对象没有被方法返回,也没有被传递给someMethod()方法外的对象。每个执行someMethod()的线程都会创 建自己的LocalObject对象,并赋值给localObject引用。因此,这里的LocalObject是线程安全的。事实上,整个 someMethod()都是线程安全的。即使将LocalObject作为参数传给同一个类的其它方法或其它类的方法时,它仍然是线程安全的。当然,如 果LocalObject通过某些方法被传给了别的线程,那它就不再是线程安全的了。

对象成员

对象成员存储在堆上。如果两个线程同时更新同一个对象的同一个成员,那这个代码就不是线程安全的。下面是一个样例:


public class NotThreadSafe{
    StringBuilder builder = new StringBuilder();
    
    public add(String text){
        this.builder.append(text);
    }	
}

如果两个线程同时调用同一个NotThreadSafe实例上的add()方法,就会有竞态条件问题。例如:


NotThreadSafe sharedInstance = new NotThreadSafe();

new Thread(new MyRunnable(sharedInstance)).start();
new Thread(new MyRunnable(sharedInstance)).start();

public class MyRunnable implements Runnable{
  NotThreadSafe instance = null;
  
  public MyRunnable(NotThreadSafe instance){
    this.instance = instance;
  }

  public void run(){
    this.instance.add("some text");
  }
}

注意两个MyRunnable共享了同一个NotThreadSafe对象。因此,当它们调用add()方法时会造成竞态条件。

当然,如果这两个线程在不同的NotThreadSafe实例上调用call()方法,就不会导致竞态条件。下面是稍微修改后的例子:


new Thread(new MyRunnable(new NotThreadSafe())).start();
new Thread(new MyRunnable(new NotThreadSafe())).start();

现在两个线程都有自己单独的NotThreadSafe对象,调用add()方法时就会互不干扰,再也不会有竞态条件问题了。所以非线程安全的对象仍可以通过某种方式来消除竞态条件。

线程控制逃逸规则

线程控制逃逸规则可以帮助你判断代码中对某些资源的访问是否是线程安全的。


如果一个资源的创建,使用,销毁都在同一个线程内完成,
且永远不会脱离该线程的控制,则该资源的使用就是线程安全的。

资源可以是对象,数组,文件,数据库连接,套接字等等。Java中你无需主动销毁对象,所以“销毁”指不再有引用指向对象。

即使对象本身线程安全,但如果该对象中包含其他资源(文件,数据库连接),整个应用也许就不再是线程安全的了。比如2个线程都创建了各自的数据库连接,每个连接自身是线程安全的,但它们所连接到的同一个数据库也许不是线程安全的。比如,2个线程执行如下代码:


检查记录X是否存在,如果不存在,插入X

如果两个线程同时执行,而且碰巧检查的是同一个记录,那么两个线程最终可能都插入了记录:


线程1检查记录X是否存在。检查结果:不存在
线程2检查记录X是否存在。检查结果:不存在
线程1插入记录X
线程2插入记录X

同样的问题也会发生在文件或其他共享资源上。因此,区分某个线程控制的对象是资源本身,还是仅仅到某个资源的引用很重要


文章转自 并发编程网-ifeve.com

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
java安全编码指南之:线程安全规则
如果我们在多线程中引入了共享变量,那么我们就需要考虑一下多线程下线程安全的问题了。那么我们在编写代码的过程中,需要注意哪些线程安全的问题呢?
2650 0
Servlet中的线程安全
        Servlet 之所有效率比CGI高,是因为CGI每一个请求都会FORK出一个进程来处理相应的请求,而Servlet则是用线程来处理的,但随之而来就有一个问题:多线程的安全问题!       在Servlet中,类的成员变量是共享的,所有的请求都共用成员变量,为了保证线程安全,我们应该尽量做到如下几点:      1.用方法变量,即局部变量来保存请求中的专有数据,因为每个线程都会有自己的一份局部变量,这样就不会相互影响了。
729 0
线程安全
什么是线程安全,个人在线程中使用共享资源时,能够保证共享资源在任何时候都是原子的、一致的,这样的线程就是线程安全的线程 首先来介绍一下共享资源的类型(这是我自己分类的,为了后文好解释),共享资源从其类型可以分为三类(下文讲到变量一律指类变量或实例变量,不再特别指出): 1.
857 0
+关注
行者武松
杀人者,打虎武松也。
17142
文章
2569
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载