局部变量总是线程安全的。但是请记住,局部变量所指向的对象可能不是这样。如果对象是在方法中示例化的,并且从不转义,那么就不会有问题。
我对java中的多线程还不熟悉,我不明白“对象转义方法”是什么意思。有人能给我举一个这个语句的代码例子吗?在这个例子中,对象通过转义方法变成了非线程安全的。另外,请解释为什么它成为非线程安全的
这是否意味着如果我们简单地将这个对象传递给另一个方法,它将成为非线程安全的?
注:这听起来像是一个简单的问题,你们许多人。但对我来说,我读了多篇文章来查看一个代码示例,但找不到它。
4条答案
按热度按时间izj3ouym1#
如果你一点一点地看,应该不难理解。
如果程序中的两个或多个线程访问相同的数据(也称为“共享数据”),那么您需要确保代码以“线程安全”的方式访问数据。
java编程语言与c++等其他语言不同,它使得线程无法共享局部变量。唯一可以共享的变量是
static
变量和共享对象的成员变量(又称“字段”)。在函数调用中将对象引用作为参数传递本身并不允许对象引用“转义”到另一个线程。但它可以允许。这完全取决于被调用函数对您传递的对象做了什么。如果在多线程程序中编写调用函数的代码,那么您有责任知道函数将如何处理您提供给它的对象(例如,函数是否将与另一个线程共享对象)
对象共享的某些方式:
将对对象的引用存储到
static
线程共享的变量,对象是
Runnable
你给一个新的Thread
,或者它是Runnable
对象的成员变量,对象是一个
Runnable
或者Callable
提交到线程池,或者是提交到线程池的对象的成员变量的引用,对象是以前共享的某个其他对象的成员变量的引用,包括,尤其是,
将对象添加到共享的容器(例如
List
或者Set
或者Map
,你把物体放进
Queue
特别是为在线程之间共享对象而设置的。hjzp0vay2#
这是否意味着如果我们简单地将这个对象传递给另一个方法,它将成为非线程安全的?
对。
嗯,它可能会变成非线程安全的:这取决于其他方法对它做了什么。
非线程安全的示例如下:
这是线程不安全的,因为其他线程现在可以通过字段访问局部变量引用的对象。
当然,如果值是可变的,它只能成为线程不安全的。一个不可变的值本质上是线程安全的,所以它可以被传递,存储在字段中,无论什么。
一个仍然线程安全的例子可以是:
这仍然是线程安全的(如果使用局部变量作为参数调用),因为
param
以线程限制的方式使用(当前执行线程之外的任何东西都看不到它的值)。您甚至可以在方法中对其进行变异:
尽管发生了变异,但这仍然是线程安全的,因为仍然只有当前线程才能看到值。
sauutmhj3#
java是一种基于引用的语言。那只是指针的另一个词。
当你写:
这只是语法:
第1行声明了一个名为“foo”的变量(指针)。第2行做了两件事:它创建了一个全新的对象。
foo
然后更新以引用它。就像"hello"
是宝藏foo
是一张藏宝图。foo = "Hello"
创造新的宝藏,把它埋在沙子里,然后在你的纸上画一张宝藏的Map;你贴上标签的那张纸foo
. "“foo”不是宝藏,说这个字符串是“foo”字符串是不正确的。不是-只是宝藏,宝藏没有名字。不可能有Map指向它(这意味着垃圾收集器最终会将它挖出来并将其清除),可能有上千个Map指向它,以及介于两者之间的任何东西。在上面的代码片段中,有一个Map指向它。你的Map。你打电话的那个foo
.但你可以和其他人分享这张Map。然后他们也能找到你的宝藏。
“局部变量是不可变的”。对。他们是。不过,你当地人是这样认为的
foo
变量,以及foo
不是宝藏。是Map。这是你的Map。没人能搞乱它。唯一能让Map看起来与众不同的方法就是你写下foo =
在你自己的代码里。没有多少过关foo
使用其他方法会改变Map。你读到的文字所指的是,唯一不变的东西就是你的Map。Map指向的宝藏?谁知道呢。如果你把Map交给其他人,他们不能改变你的Map,但他们可以复制它。他们可以跟着。他们可以挖到宝藏,然后把它砸碎。它们不会影响你的Map,但是如果你按照你的Map走,你会发现什么?如果你和其他人分享宝藏的位置,现在可能完全不同了。
现在,对于琴弦来说,这是一个没有实际意义的观点:琴弦的宝藏是不可战胜的。它们不能以任何方式被粉碎或修改。它们是不可变的对象,没有改变它的方法,也没有公共(非final)字段。但并非所有物体都是这样。有些宝藏可以换,也可以打碎。例如,一个简单的列表:
在上面的代码中,您不知道它打印什么,因为您不知道foo做什么。你知道你的
x
无法打印null
. 空是指你有一张空白的藏宝图,没有人能弄乱你的藏宝图。但是foo
可以捣乱宝藏:这没什么用。这个方法可以得到一份藏宝图。然后它创造新的宝藏,把它埋在沙子里,把一块橡皮擦放到它的口袋里
x
藏宝图(以前是你的一个副本),然后在上面画一个全新的藏宝图,其中x表示新创建的宝藏的位置。这对你的Map,或者你的Map所指向的宝藏,绝对没有任何影响。您的代码将继续打印[Hello]
.然而:
这是完全不同的。这是你的藏宝图,你给它的副本,跟随它并向下挖掘(
.
javaese的意思是:跟着Map挖)。然后它打开箱子,把“世界”放在里面(从技术上讲,它把“世界”宝藏的Map放在里面,字符串也是对象,因此是基于参考的)。如果你以后跟着Map挖,你会看到的。你的代码会打印出来
[Hello, World]
.这就是课文所说的。通过复制,或者使用不可变的对象,或者意识到如果您共享了Map,Map所指向的任何宝藏都可能已被其他代码更改,来避免这种情况。
v1uwarro4#
转义方法意味着:它被使用或存储在方法之外的某个地方。
示例:您有一个全局变量计数器,并在本地方法中计算一些内容。现在只要使用本地计数器,一切都很好,但是只要设置globalcounter=localcounter,就不能假定globalcounter==localcounter,因为它总是可以被外部因素更改。