Java 当使用非线程安全类时,创建多个对象并将其传递到每个线程是可行的设计吗?
假设我有一个不是线程安全的类A,我想用类B启动一组线程,将类A的新实例传递给类B的每个线程是否是一个好的设计选择,因为如果我将类A的一个实例传递给所有线程,就会出现并发问题Java 当使用非线程安全类时,创建多个对象并将其传递到每个线程是可行的设计吗?,java,multithreading,Java,Multithreading,假设我有一个不是线程安全的类A,我想用类B启动一组线程,将类A的新实例传递给类B的每个线程是否是一个好的设计选择,因为如果我将类A的一个实例传递给所有线程,就会出现并发问题 @NotThreadSafe class A { ... } class B extends Thread { private A a; B(A a) { this.a = a; } } class C { public static void main(String [] ar
@NotThreadSafe
class A {
...
}
class B extends Thread {
private A a;
B(A a) {
this.a = a;
}
}
class C {
public static void main(String [] args) {
for(int i = 0; i < 10; i++) {
A a = new A();
B b = new B(a);
b.start();
}
}
}
@NotThreadSafe
甲级{
...
}
B类扩展线程{
私人A;
B(A){
这个a=a;
}
}
C类{
公共静态void main(字符串[]args){
对于(int i=0;i<10;i++){
A=新的A();
B=新的B(a);
b、 start();
}
}
}
这在很大程度上取决于A类的特征
通常,通过创建每个线程的实例来解决(不是解决,而是消除)并发问题。
假设类A有一些内部状态(否则类A本身应该是线程安全的),每个线程B将读取/更新内存中属于类A“自己的”对象的不同字段,因此这里没有并发问题,除非
每次使用一个实例和一个新实例之间有很大的区别。(例如,如果类代表一个人,那么每次都是同一个人或不同的人之间的差异。这使得程序结果在不同情况下不同,因此这个问题没有意义。) 如果我们假设该类不包含任何数据,因此无论使用单个实例还是多个实例,程序结果都不会改变,那么 这是一个很好的设计选择,因为如果我将类a的一个实例传递给所有 其中会有并发问题 这将是一个糟糕的设计选择,不能解决任何并发性问题,因为每个类实例所处理的外部数据仍然是相同的,线程将争夺数据
要解决并发问题,您必须使用同步。假设您已覆盖
B
类中的run
。否则你的例子就没有意义了。(调用start()
会调用Thread::run
,它会立即返回。Ooops!)
首先是一些术语
- 线程受限对象是一个对象,其状态可由一个且仅一个线程访问
- 如果一个对象是线程受限的,那么这个类是否是线程安全的并不重要李>
a
,它在main中创建,然后仅在单个子线程中使用
- 在调用
之前,每个start()
线程都被限制在主线程中。对于程序的这一部分,线程安全(属于a
)并不重要A
- 一旦控件到达子线程的
方法,每个run()
线程就被限制在子线程中。从那时起,线程安全就不重要了a
start()
调用本身
Java内存模型(JLS 17.4)指定主线程中对start()
的调用与子线程中对run()
的调用之间存在一种before关系。这种情况发生在A
或B
实例或主线程在start()
调用之前所做的任何其他操作之前,确保在进入run
方法主体时/之后,子线程可以看到这些操作。这是强有力的保证。任何兼容Java 5.0或更高版本的实现在任何平台上都将以这种方式运行
但是,在调用start()
之后,主线程中对A
等所做的更改不能保证对子线程可见。它们可能是,也可能不是。如果发生这种情况,您的代码可能会被破坏。但是示例代码没有显示它的发生
总之,这种方法是线程安全的。它是否“可行”取决于您的设计是否满足要求。例如,
B
线程需要共享状态,这种方法不能解决这个问题
最后,扩展
线程通常被认为是个坏主意。更好的方法是实例化普通的线程
实例,将可运行的
作为构造函数参数传递给它们。或者,使用ExecutorService
,让它负责线程管理。是什么让A
不安全?是因为它是可变的并且B
改变了a
的状态吗?你需要提供a的一个实现。这个问题太抽象了,找不到具体的答案。是的,我的代码实现了run,我只是为了简洁而省略了它。