[Java 并发编程] 8. 线程安全和共享资源

tech2024-10-26  21

文章目录

前言一、Local variables (本地变量/局部变量)二、Local Object References (本地对象引用/局部对象引用)三、Object Member Variables (对象成员变量)四、线程安全规则


前言

线程安全:代码同时被多个线程安全地调用。如果一段代码是安全的,那它不包含竞争条件。竞争条件只发生在多个线程更新共享资源的时候,因此当Java线程执行的时候,知道哪些资源是线程共享资源是非常重要的。


一、Local variables (本地变量/局部变量)

局部变量保存在每个线程独有的线程栈中,因此局部变量在线程之间是不共享的。也就是说所有的局部变量都是线程安全的。举例:

public void someMethod(){ long threadSafeInt = 0; threadSafeInt++; }

二、Local Object References (本地对象引用/局部对象引用)

本地对象引用有所不同,引用本身是不共享的,同样也是保存在线程独有的线程栈中,线程之间不共享引用。但是引用的对象不是保存在线程栈中,而是保存在主内存堆中,理论上讲所有的线程都能够访问内存堆中存储的对象(但是要有对象的引用)。

如果一个对象创建之后没有离开创建它的方法,那么是线程安全的。事实上传递这个对象的引用给其他的方法,只要这个对象的引用没有传递给其他的线程,那么这个对象都不会成为共享对象,始终是线程安全的。示例:

public void methodOne(){ LocalObject localObject = new LocalObject(); methodTwo(localObject); } public void methodTwo(LocalObject localObject){ localObject.setValue("value"); }

示例中:对象 localObject 在 methodOne() 方法中被创建,然后传递给 methodTwo(),localObject 没有传递给其他线程; 每个线程执行 methodOne() 时会都创建一个新的 LocalObject 对象,且 LocalObject 对象的引用都保存在各自的线程栈中,因此是 methodOne() 线程安全的,尽管 LocalObject 存在多个实例对象,但使用它们是线程安全的。

但有一种场景例外: 当某个方法将 localObject 对象的引用作为参数传递给了其他线程,那么可能会造成线程不安全。


三、Object Member Variables (对象成员变量)

对象成员变量随对象保存在堆内存中。因此当两个线程调用了某个方法,这个方法引用了同一个对象并修改了这个对象的成员变量时,那么这个方法时线程不安全的。示例:

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"); } }

请注意有 2 个 MyRunnable 对象共享了 sharedInstance 对象,因此当他们同时调用 sharedInstance.add() 方法时,会发生竞争条件。

然而,当 2 个线程同时调用不对对象的 add() 方法时,不会产生竞争条件。示例

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

四、线程安全规则

当你想确认你的代码访问某些资源的时候是否是线程安全的,你可以使用下面这个规则:

如果资源的创建、使用和销毁都没有离开某个方法,并且没有分享给其他线程,那么使用这个资源是线程安全的。

最新回复(0)