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