java线程同步失败

java线程同步失败,java,multithreading,makefile,Java,Multithreading,Makefile,Java在线程同步方面似乎不是100%准确。本例中的代码打印每个线程递增的静态整数的值。如果输出多次包含相同的数字,uniq将识别它。每个示例都由Makefile脚本运行,以帮助说明问题。每个示例都使用不同的同步/锁定方法,但似乎没有一种方法在100%的时间内都能正常工作。大多数复制发生在循环的早期,至少在这个系统上 生成文件: JAVA=/usr/local/jdk/bin/java JAVAC=$(JAVA)c build: $(JAVAC) Synchron.java $

Java在线程同步方面似乎不是100%准确。本例中的代码打印每个线程递增的静态整数的值。如果输出多次包含相同的数字,uniq将识别它。每个示例都由Makefile脚本运行,以帮助说明问题。每个示例都使用不同的同步/锁定方法,但似乎没有一种方法在100%的时间内都能正常工作。大多数复制发生在循环的早期,至少在这个系统上

生成文件:

JAVA=/usr/local/jdk/bin/java
JAVAC=$(JAVA)c

build:
    $(JAVAC) Synchron.java
    $(JAVAC) SynchronVolatile.java
    $(JAVAC) SynchronFinal.java
    $(JAVAC) SynchronThis.java
    $(JAVAC) SynchronA.java
    $(JAVAC) SynchronObj.java

run:
    $(JAVA) Synchron | sort | uniq -c | egrep -v '^\s+1\s+' ; /bin/true
    $(JAVA) SynchronVolatile | sort | uniq -c | egrep -v '^\s+1\s+' ; /bin/true
    $(JAVA) SynchronFinal | sort | uniq -c | egrep -v '^\s+1\s+' ; /bin/true
    $(JAVA) SynchronThis | sort | uniq -c | egrep -v '^\s+1\s+' ; /bin/true
    $(JAVA) SynchronA | sort | uniq -c | egrep -v '^\s+1\s+' ; /bin/true
    $(JAVA) SynchronObj | sort | uniq -c | egrep -v '^\s+1\s+' ; /bin/true
Synchron.java:

import java.io.*;
import java.util.*;

public class Synchron implements Runnable {
        static int a;

        synchronized public void adder() {
        Synchron.a++;
        System.out.println( Synchron.a );
        }

        public void run() {
                while( Synchron.a < 65535 ) {
                        adder();
                }
        }
        public static void main( String []args ) {
                ArrayList <Thread>al = new ArrayList<Thread>();

                try {
                        int i;
                        for( i = 0; i<10 ; i++ ) {
                                Synchron s = new Synchron();
                                Thread t = new Thread( s );
                                al.add(t);
                                t.start();
                        }

                        for( Thread t : al ) {
                                t.join();
                        }
                }
                catch( Exception e ) {
                        e.printStackTrace();
                }

        }
}
import java.io.*;
导入java.util.*;
公共类Synchron实现可运行{
静态int a;
同步公共无效加法器(){
Synchron.a++;
System.out.println(Synchron.a);
}
公开募捐{
而(同步a<65535){
加法器();
}
}
公共静态void main(字符串[]args){
ArrayList al=新的ArrayList();
试一试{
int i;

对于(i=0;i您的问题是,在某些情况下,您的锁锁定在不同的锁对象实例上,因此它们实际上不会干扰其他锁对象实例

改变

Object o;

现在,所有
synchronized
语句都将尝试锁定同一个对象,并且将发生正确的锁争用

这看起来也很可疑:

while (SynchronObj.a < 65535) {...}
while(SynchronObj.a<65535){…}
由于您正在读取a的值,而没有同步。这肯定是个问题

您测试同步的方法似乎也是通过搜索打印的重复输出

    public void run() {
            for (int i=0; i<10000; i++) {
                    adder();
            }
    }
public void run(){

对于(int i=0;iMartin Konecny指出了代码中的一个主要问题。所有线程必须满足于同一个锁对象才能正确同步。这就是为什么您的尝试
Synchron
SynchronThis
SynchronObj
无法工作的原因

然而,另一个问题是,你需要

System.out.println(SynchronObj.a)

发生在同步块之外。这也会导致一些问题。请考虑以下可能的执行方案:

让我们假设“a”的值是30。 线程1进入方法adder()并锁定对象o。它递增“a”并释放锁。现在
a==31
。但是,在它打印“a”的值之前,线程1暂停,线程2开始执行。线程2获得锁,递增“a”然后释放锁。现在
a==32
。线程2继续执行并调用

System.out.println(SynchronObj.a)
屏幕上打印的值从30到32。31被“吞没”。因此,请确保您也从同步块中打印。始终同步共享变量的读写。即使您认为可能没有必要,编译器也可能会重新排列您的代码。有关此行为的详细信息,请阅读

您还应该避免对while循环中的“a”进行非同步访问。一种替代方法是:

static int a;
static Object o = new Object();
static boolean cont = true;

public void adder() {
    synchronized( o ) {
        if (cont) {
            MainClass.a++;
            System.out.println( MainClass.a );
            cont = (MainClass.a < 65535);
        }
    }
}

public void run() {
    while(MainClass.cont) {
        adder();
    }
}
static int a;
静态对象o=新对象();
静态布尔值cont=true;
公共无效加法器(){
同步(o){
如果(续){
MainClass.a++;
系统输出打印LN(主类a);
cont=(主类a<65535);
}
}
}
公开募捐{
while(MainClass.cont){
加法器();
}
}

最后,您不需要将锁对象声明为
volatile
volatile
关键字告诉JVM不要在本地缓存变量值线程。而应该从内存中读取当前值。这会在同步块中自动发生。有关volatile的更多信息也在中。

也可能会更改方法
加法器()
在没有同步对象的情况下进行静态同步未同步读取可能是一个问题,但对于作者正在检查的特定条件,只要加法器的同步工作正常,就不会产生问题。在实际情况下,例如在无锁并发数据结构W,将打印放在同步部分之外对我来说都是一件非常愚蠢的事情。
while (SynchronObj.a < 65535) {...}
    public void run() {
            for (int i=0; i<10000; i++) {
                    adder();
            }
    }
System.out.println(SynchronObj.a)
System.out.println(SynchronObj.a)
static int a;
static Object o = new Object();
static boolean cont = true;

public void adder() {
    synchronized( o ) {
        if (cont) {
            MainClass.a++;
            System.out.println( MainClass.a );
            cont = (MainClass.a < 65535);
        }
    }
}

public void run() {
    while(MainClass.cont) {
        adder();
    }
}