线程安全与共享资源

原文链接 作者:Jakob Jenkov 译者:毕冉 校对:丁一

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

局部变量

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

查看源代码

打印帮助

1 public void someMethod(){
2    
3   long threadSafeInt = 0;
4  
5   threadSafeInt++;
6 }

局部的对象引用

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

查看源代码

打印帮助

01 public void someMethod(){
02    
03   LocalObject localObject = new LocalObject();
04  
05   localObject.callMethod();
06   method2(localObject);
07 }
08  
09 public void method2(LocalObject localObject){
10   localObject.setValue("value");
11 }

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

对象成员

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

查看源代码

打印帮助

1 public class NotThreadSafe{
2     StringBuilder builder = new StringBuilder();
3      
4     public add(String text){
5         this.builder.append(text);
6     }  
7 }

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

查看源代码

打印帮助

01 NotThreadSafe sharedInstance = new NotThreadSafe();
02  
03 new Thread(new MyRunnable(sharedInstance)).start();
04 new Thread(new MyRunnable(sharedInstance)).start();
05  
06 public class MyRunnable implements Runnable{
07   NotThreadSafe instance = null;
08    
09   public MyRunnable(NotThreadSafe instance){
10     this.instance = instance;
11   }
12  
13   public void run(){
14     this.instance.add("some text");
15   }
16 }

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

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

查看源代码

打印帮助

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

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

线程控制逃逸规则

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

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

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

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

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

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

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

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

时间: 2024-10-31 13:08:37

线程安全与共享资源的相关文章

1线程概念:线程和进程之间的关系,线程间可共享资源,线程间非共享资源,线程的优缺点

 1线程概念 1.1什么是线程 1.1.2线程和进程的关系         1.轻量级进程(light-weightprocess),也有PCB,创建线程使用底层函数和进程一样,都是clone. 2.从内核里看进程和线程是一样的,都有各自不同的PCB,但是PCB中指向内存资源的三级页表示相同的 3.进程可以蜕变成线程 4.在美国人眼里,线程就是寄存器和栈 5.在Linux下,线程是最小的执行单位:进程是最小的分配资源的单位   查看LWP号(通过这个命令可以查看到线程号),命令是: ps –

Java 并发/多线程教程(九)-线程安全和共享资源

         本系列译自jakob jenkov的Java并发多线程教程,个人觉得很有收获.由于个人水平有限,不对之处还望矫正!       代码被多个线程同时调用是安全的,那么就称之为线程安全.如果一段代码是线程安全的,那么它没有竞态条件.竞态条件只有发生在多个线程更新共享资源.因些,清楚的知道线程执行时什么资源是共享的非常重要. 本地变量        本地变量存储在每个线程自己的栈里,这就意味着本地变量从不与其他线程共享.也就是说本地变量是线程安全的,下面是关于线程安全的本地变量的一个

Java如何共享资源

对一种特殊的资源--对象中的内存--Java提供了内建的机制来防止它们的冲突.由于我们通常将数据元素设为从属于private(私有)类,然后只通过方法访问那些内存,所以只需将一个特定的方法设为synchronized(同步的),便可有效地防止冲突.在任何时刻,只可有一个线程调用特定对象的一个synchronized方法(尽管那个线程可以调用多个对象的同步方法).下面列出简单的synchronized方法: synchronized void f() { /* ... */ } synchroni

java多线程并发中使用Lockers类将多线程共享资源锁定_java

复制代码 代码如下: package com.yao; import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReadWriteLock;import java.util.c

Windows7家庭组局域网共享资源

还记得大学时代和宿舍里的同学想要建一个局域网玩游戏,但那时候大家都用WinXP系统,那可不是连几根网线就完成的事情啊,设置都要折腾半天,必须要有真正的电脑高手指导才有可能实现.现在很多朋友家中电脑多了,想要联网在一起共享资源方便,组建一个家庭网络,如果你还是用WinXP系统那同样是个艰难的工程.不过可能你听说身边好多朋友都有了家庭网,没觉得他们是电脑高手啊?那毫无疑问是Windows7系统帮助了他们.Windows7家庭高级版.Win7旗舰版.Win7专业版都支持强大且易用的家庭组网功能,说菜鸟

局域网共享资源探测子系统设计代码有没有?

问题描述 局域网共享资源探测子系统设计代码有没有? 要求n 搜索指定的某个计算机的共享资源n 搜索所有计算机的所有共享资源n 将某个指定的共享目录映射到本地磁盘(映射网络驱动器)n 将搜索到的共享资源列表导出到文本文件n 对搜索结果进行排序

用“$”来加强共享资源的安全

每当谈到安全这个话题时,有许多人往往表现得很淡然,他们往往会认为"安全"只是针对那些大中型企事业单位的,而与自己没有什么关系.其实,在一无法纪二无制度的虚拟网络世界中,现实生活中所有的阴险和卑鄙都表现得一览无余,在这样的信息时代里,几乎每个人都面临着安全威胁,都有必要对网络安全有所了解,并能够处理一些安全方面的问题,那些平时不注意安全的人,往往在受到安全方面的攻击时,付出惨重的代价时才会后悔不已.为了把损失降低到最低限度,我们一定要有安全观念,并掌握一定的安全防范措施,下面笔者就介绍几

访问Windows 2008共享资源出错解决方法

为了能够更方便地与局域网中的其他员工相互交流,我们时常会通过共享访问方式,来与他人互相传输文件.发送数据.为了让共享访问操作更加更效,我们还总结了许多行之有效的操作秘籍,可是这些秘籍几乎都是在传统系统环境下总结出来的,它们能不能在Windows Server 2008系统环境下有效呢?其实Windows Server 2008系统是一种与众不同的操作环境,在该环境下沿用传统秘籍肯定会遇到一些奇怪的共享访问故障:为此,我们现在就对Windows Server 2008环境下出现的一些常见共享访问错

Windows中快速访问分散共享资源的方法

一般来说,通过网上邻居窗口或"\\主机名\文件夹的共享名"这样的方式,就能轻易访问到某台主机上的指定共享文件夹资源.不过,倘若自己想要访问的共享资源分布在不同的计算机中,或者分布在不同的共享文件夹中的话,那么此时再按照前面的方法来访问共享资源的话,访问效率肯定会大打折扣.那有没有办法对那些分散在各个地方上的共享资源进行快速访问呢?答案是肯定的,下面就是笔者总结出来的两种快速访问分散共享资源的妙招! 1.DFS链接法 所谓DFS链接法,其实就是利用Windows 2003操作系统内置的分