简体   繁体   English

java线程同步失败

[英]java thread synchronization fails

Java does not appear to be 100% accurate with thread synchronization. 使用线程同步,Java似乎不是100%准确。 The code in this example prints the value of a static integer which is incremented by each thread. 此示例中的代码打印一个静态整数的值,该值由每个线程递增。 Should the output include the same number more than once uniq will identify it. 如果输出包含相同的数字,uniq将多次识别它。 Each of the examples is run by the Makefile script to help illustrate the problem. 每个示例都由Makefile脚本运行,以帮助说明问题。 Each example uses a different method of synchronization/locking, yet none appears to work well 100% of the time. 每个示例使用不同的同步/锁定方法,但似乎没有一个方法在100%的时间内都能正常工作。 Most of the duplication happens early on in the loop, on this system at least. 大多数复制发生在循环的早期,至少在这个系统上。

Makefile: Makefile文件:

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: 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();
                }

        }
}

SynchronVolatile.java: SynchronVolatile.java:

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

public class SynchronVolatile implements Runnable {
        static int a;
    static volatile Object o = new Object();

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

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

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

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

        }
}

SynchronFinal: This is the same as SynchronVolatile.java, except it uses a final for Object o, rather than volatile. SynchronFinal:这与SynchronVolatile.java相同,除了它使用Object o的final,而不是volatile。

SynchronThis.java: SynchronThis.java:

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

public class SynchronThis implements Runnable {
        static int a;
    static volatile Object o = new Object();

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

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

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

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

        }
}

SynchronA.java: SynchronA.java:

import java.io.*;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SynchronA implements Runnable {
        static int a;
    private volatile Lock lock = new ReentrantLock();

        public void adder() {
        lock.lock();
        SynchronA.a++;
        System.out.println( SynchronA.a );
        lock.unlock();
        }

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

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

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

        }
}

SynchronObj.java: SynchronObj.java:

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

public class SynchronObj implements Runnable {
        static int a;
    Object o;

    public SynchronObj( Object obj ) {
        o = obj;
    }

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

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

        final Object o = new Object();

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

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

        }
}

When this runs, none of the methods of thread synchronization above work 100% of the time. 当它运行时,上面的线程同步方法都没有100%的时间工作。 Any ideas of what could be going wrong? 什么可能出错?

Your issue is that in some cases your locks are locking on different lock object instances, so they actually never interfere with other. 您的问题是,在某些情况下,您的锁定锁定在不同的锁定对象实例上,因此它们实际上从不干扰其他锁定。

Change 更改

Object o;

to

public static final Object o = new Object();

Now all your synchronized statements will try to lock on the same object, and the correct lock contention will occur. 现在所有的synchronized语句都会尝试锁定同一个对象,并且会发生正确的锁争用。

Also this looks suspect: 这看起来很可疑:

while (SynchronObj.a < 65535) {...}

since you are reading the value of a without synchronization . 因为你正在读取没有同步的a的值 Definitely an issue. 绝对是一个问题。

It also seems that your method of testing synchronization is by searching for duplicate outputs printed. 您的同步测试方法似乎也是通过搜索打印的重复输出。 Instead, try doing 相反,尝试做

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

Because you are running 10 threads, simply verify if the final answer is 10000*10 . 因为您运行10个线程,只需验证最终答案是否为10000*10 Anything less/more will imply incorrect thread synchronizatoin. 任何更少/更多将暗示不正确的线程同步。

Martin Konecny has pointed out one major issue with your code. Martin Konecny指出了您的代码存在的一个主要问题。 All Threads must content for the same lock Object for proper synchronization. 所有线程必须满足相同的锁定对象才能正确同步。 This is why your attempts Synchron , SynchronThis and SynchronObj are not going to work. 这就是您尝试SynchronSynchronThisSynchronObj无法正常工作的原因。

Another issue, however, is that your call to 然而,另一个问题是你的电话

System.out.println(SynchronObj.a)

occurs outside the synchronized block. 发生在同步块之外。 This will also lead to some problems. 这也会导致一些问题。 Consider following possible execution scenario: 考虑以下可能的执行方案:

Let us assume the value of "a" is 30. Thread 1 enters method adder() and locks on Object o. 让我们假设“a”的值是30.线程1进入方法adder()并锁定Object o。 It increments "a" and releases the lock. 它增加“a”并释放锁定。 Now a == 31 . 现在a == 31 Before it prints the value of "a", however, Thread 1 is paused and Thread 2 begins its execution. 然而,在打印“a”的值之前,线程1暂停并且线程2开始执行。 Thread 2 obtains the lock, increments "a" and releases the lock. 线程2获得锁定,递增“a”并释放锁定。 Now a == 32 . 现在a == 32 Thread 2 continues with its execution and calls 线程2继续执行并调用

System.out.println(SynchronObj.a)

The values printed on screen went from 30 to 32. 31 was "swallowed". 屏幕上打印的值从30到32.31被“吞下”。 So make sure that you also print from within your synchronized block. 因此,请确保您也在同步块中打印。 Always synchronize read and writes of shared variables. 始终同步共享变量的读写。 Even if you think that it might be not necessary, the compiler might rearrange your code. 即使您认为可能没有必要,编译器也可能会重新排列您的代码。 Read here for more info on this behavior. 请阅读此处以获取有关此行为的更多信息

You should also avoid the non-synchronized access of "a" in the while loop. 您还应该避免在while循环中非​​同步访问“a”。 One alternative would be: 一种选择是:

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();
    }
}

Finally, you do not need to declare your lock object as volatile . 最后,您不需要将lock对象声明为volatile The volatile keyword tells the JVM to not cache a variables value thread-locally. volatile关键字告诉JVM不在线程本地缓存变量值。 The current value shall instead be read from memory. 而应从存储器中读取当前值。 This happens automatically inside a synchronized block. 这在同步块内自动发生。 More info on volatile also in Chapter 17.4 Memory Model . 有关volatile的更多信息,请参见第17.4章内存模型

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM