Java 如何围绕非';没有适当出版?
我正在阅读“实践中的Java并发”,并查看第51页上的示例代码 根据这本书,如果这段代码没有正确发布,它就有失败的风险。因为我喜欢编写示例并将其分解以证明它们是如何工作的。我试图让它抛出断言错误,但失败了。(带我到我的房间) 任何人都可以发布示例代码以便抛出AssertionError吗?规则:不要修改Holder类Java 如何围绕非';没有适当出版?,java,concurrency,Java,Concurrency,我正在阅读“实践中的Java并发”,并查看第51页上的示例代码 根据这本书,如果这段代码没有正确发布,它就有失败的风险。因为我喜欢编写示例并将其分解以证明它们是如何工作的。我试图让它抛出断言错误,但失败了。(带我到我的房间) 任何人都可以发布示例代码以便抛出AssertionError吗?规则:不要修改Holder类 public class Holder{ private int n; public Holder(int n){ this.n = n;
public class Holder{
private int n;
public Holder(int n){
this.n = n;
}
public void assertSanity(){
if (n != n) {
throw new AssertionError("This statement is false");
}
}
}
我已经修改了这个类,使它更脆弱,但仍然无法抛出断言错误
class Holder2 {
private int n;
private int n2;
public Holder2(int n) throws InterruptedException{
this.n = n;
Thread.sleep(200);
this.n2 = n;
}
public void assertSanity(){
if (n != n2) {
throw new AssertionError("This statement is false");
}
}
}
是否可以使上述任何一个类抛出断言错误?或者我们必须接受他们偶尔会这样做,而我们无法编写代码来证明这一点吗?您不能通过使用以下命令随时更改
n
的值:
Holder h = new Holder(5);
Field f = h.getClass().getDeclaredField("n");
f.setAccessible(true);
f.setInt(h, 10);
h.assertSanity();
我会在一台多处理器机器上运行几个小时,看看会发生什么(如果你使用你的支架,请移除睡眠)。这样的竞争条件可能很少见,或者在你的特定机器上不存在——但至少要通过数百万次的尝试,在一百万个案例中挑起这种情况
class Checker {
private Holder h;
public Checker() {
h = new Holder(42);
}
public void check() {
h.assertSanity();
}
public void create(int n) {
h = new Holder(n);
}
}
public class MyThread extends thread{
private bool check;
private final Checker c;
public MyThread(bool check,Checker c) {
this.check = check;
this.c = c;
}
public static void main(String[] args) {
Checker c = new Checker();
MyThread t1 = new MyThread(false,c);
MyThread t2 = new MyThread(true,c);
t1.start();
t2.start();
t1.join();
t2.join();
}
public void run() {
int n = 0;
while(true) {
if(check)
c.check();
else
c.create(n++);
}
}
}
}
正如BobbyShaftoe在另一个线程中所说的,您不能仅仅依靠运行代码足够的时间来证明错误可能发生或不可能发生。如果您从汇编级别来考虑这一点,那么n将非常困难!=n因为呼叫太少,并且依赖于进程在非常精确的时间被关闭 如果您希望能够显示并发系统是否可证明有效,那么最好使用标记的转换系统之类的东西对其进行建模。如果您对证明并发性或查找错误感兴趣,请尝试LTSA工具
在本例中,书中给出的Holder类不是问题的直接原因,事实上它指出: 这里的问题不是Holder类本身,而是Holder没有正确发布。但是,可以通过声明n字段为最终字段使持有者不受不适当发布的影响,这将使持有者不可变;见第3.5.2节 在此之前,它提到了以下代码,它是问题的主题:
// Unsafe publication
public Holder holder;
public void initialize() {
holder = new Holder(42);
}
因此,要重新创建它,您需要创建一个publisher类和两个线程,一个调用initialize,另一个调用assert
话虽如此,我试图自己重新创建它,但还是失败了:(
下面是我的第一次尝试,不过现在有一个更好的解释
公共类持有者测试{
@试验
public void testHolder()引发异常{
对于(int i=0;i<100000000;i++){
最终倒计时闩锁完成=新倒计时闩锁(2);
最终HolderPublisher publisher=新HolderPublisher();
最终线程publisherThread=新线程(新发布者),
完成);
最终线程检查器线程=新线程(新检查器(发布者,
完成);
publisher.holder=null;
publisherRead.start();
checkerThread.start();
完成。等待();
}
}
静态类发布器实现可运行{
私人最终倒计时结束;
私人最终持有者出版商;
公共出版者(最终持有者出版者),
最终倒计时(已完成){
this.publisher=publisher;
this.finished=finished;
}
@凌驾
公开募捐{
试一试{
publisher.initialize();
}最后{
完成。倒计时();
}
}
}
静态类检查器实现可运行{
私人最终倒计时结束;
私人最终持有者出版商;
公共审核人(最终持有者发布者),
最终倒计时(已完成){
this.publisher=publisher;
this.finished=finished;
}
@凌驾
公开募捐{
试一试{
publisher.holder.assertSanity();
}捕获(最终NullPointerException e){
//这不是我们感兴趣的错误,所以请接受它
}最后{
完成。倒计时();
}
}
}
静态类保持器发布器{
//不安全出版物
公共持有人;
公共无效初始化(){
持有人=新持有人(42);
}
}
}
如果不修改Holder
类,我认为断言错误不会发生。我认为这本书是错误的
导致断言错误的唯一原因是对部分构造的对象调用assertSanity()时。。除构造函数线程外,线程如何引用部分构造的对象?好的,只有在以下两种情况下才可能:
此
。例如,将此
分配给共享变量。在我们的示例代码中不会发生这种情况,因为Holder的构造函数不会这样做public class GoodCode {
public Holder holder;
public void initialize () {
holder = new Holder(42);
}
}
如果反汇编initialize()
,则会得到以下结果:
public void initialize();
Code:
0: aload_0
1: new #2 // class Holder
4: dup
5: bipush 42
7: invokespecial #3 // Method Holder."<init>":(I)V
10: putfield #4 // Field holder:LHolder;
13: return
public void initialize();
代码:
0:aload_0
1:新的#2//类别持有者
4:dup
5:bipush 42
7:调用特殊的#3//方法持有者。“”:(I)V
10:putfield#4//字段持有者:LHolder;
13:返回
请注意,putfield holder
在invokespecial
之后执行。这意味着holder
的赋值在构造函数完成后发生。部分构造的对象仅存储在线程堆栈中。它不会发布
如果您能够以合理的方式触发断言错误
public void initialize();
Code:
0: aload_0
1: new #2 // class Holder
4: dup
5: bipush 42
7: invokespecial #3 // Method Holder."<init>":(I)V
10: putfield #4 // Field holder:LHolder;
13: return