我正在阅读Java Concurrency in Practice,并且与线程限制概念相混淆。这本书说明了
当一个对象局限于一个线程时,即使受限对象本身不是
,这种用法也会自动进行线程安全的。
因此,当一个对象被限制在一个线程中时,没有其他线程可以访问它吗?是否意味着被限制在线程中?如何将对象限制在线程中?
修改 但是,如果我仍然想与另一个线程共享该对象呢?假设在线程A完成对象O之后,线程B想要访问O.在这种情况下,在A完成后,O仍然可以被限制在B吗?
使用局部变量是一个肯定的例子,但这只是意味着你不与其他线程(AT ALL)共享你的对象。在JDBC连接池的情况下,一旦线程完成该连接,它就不会将一个连接从一个线程传递到另一个线程(因为我从未使用过JDBC,所以完全无能为力)。
答案 0 :(得分:40)
因此,当一个对象局限于一个线程时,没有其他线程可以访问它吗?
不,这是另一种方式:如果你确保没有其他线程可以访问某个对象,那么该对象被认为仅限于一个线程。
没有语言或JVM级别的机制将对象限制在单个线程中。您只需确保对该对象的引用不会转义到另一个线程可以访问的位置。有些工具有助于避免泄漏引用,例如ThreadLocal
类,但确保没有任何引用泄露到任何地方。
例如:如果对象的仅引用来自本地变量,则该对象肯定仅限于单个线程,因为其他线程永远不能访问局部变量。
类似地,如果对象的唯一引用来自另一个已被证明局限于单个线程的对象,则该第一个对象被限制在同一个线程中。
广告编辑:实际上,您可以拥有一个对象,该对象在其生命周期内一次只能由一个线程访问,但该单个线程会更改(JDBC Connection
对象从连接池是一个很好的例子。)
证明这样的对象只能由单个线程访问,这比在一生中仅限于单个线程的对象要难得多,但是。
在我看来,这些对象永远不会“局限于单个线程”(这意味着强有力的保证),但可以说“一次只能由一个线程使用”。
答案 1 :(得分:10)
最明显的例子是使用线程本地存储。请参阅以下示例:
class SomeClass {
// This map needs to be thread-safe
private static final Map<Thread,UnsafeStuff> map = new ConcurrentHashMap<>();
void calledByMultipleThreads(){
UnsafeStuff mystuff = map.get(Thread.currentThread());
if (mystuff == null){
map.put(Thread.currentThread(),new UnsafeStuff());
return;
}else{
mystuff.modifySomeStuff();
}
}
}
UnsafeStuff
对象本身“可以与其他线程共享”,如果你在运行时将一些其他线程而不是Thread.currentThread()
传递给地图的get
方法,你会得到属于其他线程的对象。但你选择不。这是“仅限于线程的用法”。换句话说,运行时条件使得对象实际上永远不会在不同的线程之间共享。
另一方面,在下面的示例中,对象自动局限于线程,因此,“对象本身”仅限于线程。从某种意义上说,无论运行时条件如何,都无法从其他线程获取引用:
class SomeClass {
void calledByMultipleThreads(){
UnsafeStuff mystuff = new UnsafeStuff();
mystuff.modifySomeStuff();
System.out.println(mystuff.toString());
}
}
这里,UnsafeStuff
在方法中分配,并在方法返回时超出范围。换句话说,Java规范静态地确保对象始终局限于一个线程。因此,确保限制不是运行时条件或使用它的方式,而是Java规范。
事实上,现代JVM有时会在堆栈上分配这样的对象,这与第一个示例不同(没有亲自检查过这个,但我认为至少目前的JVM没有这样做。)
然而换句话说,在第一个例子中,JVM无法确定对象是否仅限于一个线程内,只需查看calledByMultipleThreads()
内部(谁知道其他方法正在弄乱SomeClass.map
})。在后一个例子中,它可以。
编辑:但如果我还想要怎么做呢 与另一个线程共享对象? 让我们说在线程A完成之后 对象O,线程B想要 访问O.在这种情况下,O仍然可以 A完成后局限于B?
在这种情况下,我不认为它被称为“受限制”。执行此操作时,您只需确保不同时访问对象。这就是EJB并发的工作原理。您仍然必须将有问题的共享对象“安全地发布”给线程。
答案 2 :(得分:6)
因此,当一个对象被限制在一个线程中时,没有其他线程可以访问它吗?
这就是线程限制的含义 - 对象只能被一个线程访问。
它是否意味着被限制在线程中?
见上文。
如何将对象限制在线程中?
一般原则是不要将引用放在允许另一个线程看到它的地方。枚举一组确保这一点的规则有点复杂,但(例如)if
答案 3 :(得分:5)
我想这就是想说的。就像在run
方法中创建一个对象而不是将引用传递给任何其他实例一样。
简单示例:
public String s;
public void run() {
StringBuilder sb = new StringBuilder();
sb.append("Hello ").append("world");
s = sb.toString();
}
StringBuilder实例是线程安全的,因为它仅限于线程(执行此run方法)
答案 4 :(得分:3)
一种方法是“堆栈限制”,其中对象是局限于线程堆栈的局部变量,因此没有其他线程可以访问它。在下面的方法中,list
是局部变量,不会从方法中转义。该列表不必是线程安全的,因为它仅限于执行线程的堆栈。没有其他线程可以修改它。
public String foo(Item i, Item j){
List<Item> list = new ArrayList<Item>();
list.add(i);
list.add(j);
return list.toString();
}
将对象限制为线程的另一种方法是使用ThreadLocal
变量,该变量允许每个线程拥有自己的副本。在下面的示例中,每个线程都有自己的DateFormat
对象,因此您不必担心DateFormat
不是线程安全的事实,因为多线程不会访问它
private static final ThreadLocal<DateFormat> df
= new ThreadLocal<DateFormat>(){
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyyMMdd");
}
};
答案 5 :(得分:0)
请参阅:http://codeidol.com/java/java-concurrency/Sharing-Objects/Thread-Confinement/
更正式的维护方式 线程限制是ThreadLocal, 这允许你关联一个 具有值保持的每线程值 宾语。 Thread-Local提供get和 设置维护a的accessor方法 每个值的单独副本 使用它的线程,所以get返回 传递给set的最新值 从当前正在执行的线程。
它拥有每个线程的对象副本,线程A无法访问线程B的副本并且如果您将专门执行它则打破它的不变量(例如,将ThreadLocal值分配给静态变量或使用其他方法公开它)
答案 6 :(得分:0)
这正是它的含义。对象本身只能由一个线程访问,因此是线程安全的。 ThreadLocal
个对象是一种绑定到唯一线程的对象
答案 7 :(得分:0)
我的意思是只有在一个线程中运行的代码才能访问该对象。
在这种情况下,对象不需要是“线程安全的”