为什么synchronized()的位置很重要?

时间:2018-01-06 15:45:35

标签: java multithreading

我正在学习线程安全。我写了一个例子并得到了一个问题。

首先,我的main()函数是相同的:

public class ThreadSafe {
    public static void main(String[] args) {
        System.out.println("Thread Safe");

        SafeSharedRunnable r = new SafeSharedRunnable();

        // Access the same resource
        Thread tA = new Thread(r);
        Thread tB = new Thread(r);
        Thread tC = new Thread(r);
        Thread tD = new Thread(r);

        tA.start();
        tB.start();
        tC.start();
        tD.start();
    }
}

然后我有两个版本Runnable,其中synchronized()放在不同的地方,因此,一个版本有效,一个版本没有。

工作版本:

public class SafeSharedRunnable implements Runnable {
    int count = 5;

    @Override
    public void run() {
        // Thread Safe, must be outside of while(), why?
        synchronized ("") {
            while (count > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {

                }
                System.out.println("Current value is: " + count--);
            }
        }
    }    
}

正确的结果:

run:
Thread Safe
Current value is: 5
Current value is: 4
Current value is: 3
Current value is: 2
Current value is: 1
BUILD SUCCESSFUL (total time: 0 seconds)

非工作版本:

public class SafeSharedRunnable implements Runnable {
    int count = 5;

    @Override
    public void run() {
        while (count > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException ex) {

            }
            // Thread Safe
            synchronized ("") {
                System.out.println("Current value is: " + count--);
            }
        }
    }    
}

错误的结果:

run:
Thread Safe
Current value is: 5
Current value is: 4
Current value is: 2
Current value is: 3
Current value is: 1
Current value is: 0
Current value is: -1
Current value is: -2
BUILD SUCCESSFUL (total time: 0 seconds)

如您所见,不同的synchronized()块的不同位置会导致不同的结果。根据我的理解,关键资源冲突应该发生在这行代码上:

System.out.println("Current value is: " + count--);

但为什么我必须在while()块之外放置synchronized()?这是否意味着我应该同步所有包含变量的代码" count"?感谢您的详细解释。

我不认为这与竞争条件问题重复,因为我并不是在询问有关多线程的任何一般知识。相反,这是一个关于多线程如何进入代码流的详细问题。

3 个答案:

答案 0 :(得分:3)

“非工作版本”

的说明

如果synchronized仅包装System.out.println,那么您只能保证打印语句一次发生一个。因此,所有线程都可以在运行开始时立即整个while,然后再减少任何内容。一旦所有线程都在while循环内,它们将运行打印和递减,而不管count

“工作版本”

的说明

但是,如果您将while (count > 0) {包裹在synchronized中,那么一次只能有一个帖子进入while 。这意味着每个线程必须在执行while循环的全部内容之前检查计数,包括减量。

答案 1 :(得分:1)

您的代码问题是

mawk -v OFS='\t' '
NR==1 {
nbfield=(NF-1)
for(i=1;i<NF;i++)
    ID[i]=$(i+1)
print $1 OFS "ID" OFS "VALUE"
next
}
{
numrecord=((NR-1)%nbfield)
numrecord = numrecord ? numrecord : nbfield
for(i=0;i<=nbfield;i++)
    val[ID[i],numrecord]=$(i+1)
}
numrecord==nbfield {
for(i=1;i<=nbfield;i++)
    for(j=1;j<=nbfield;j++) 
        print val[ID[0],j] OFS ID[j] OFS val[ID[j],i]
}
' infile

这四个主题将依次打印// if four threads come here and now count is 1 synchronized ("") { System.out.println("Current value is: " + count--); } 导致输出count

试试此代码

1 0 -1 -2

虽然你的工作版本得到了正确的输出,但是你同步了while循环,这意味着只有一个线程可以完成所有工作。这不是多线程应该做的事情。

答案 2 :(得分:0)

如果你把synchronized ("")置于块中,那么多个线程同时看到count = 1并且所有都将进入while循环并减少计数并导致错误的结果。如果你使计数原子整数然后它将对所有人可见线。