Java 同步方法无法按预期工作

Java 同步方法无法按预期工作,java,multithreading,Java,Multithreading,我有一个由两个线程共享的变量。这两个线程将对其执行一些操作。我不知道为什么每次执行程序时sharedVar的结果都不一样 public class Main { public static int sharedVar = 0; public static void main(String[] args) { MyThread mt1 = new MyThread(); MyThread mt2 = new MyThread();

我有一个由两个线程共享的变量。这两个线程将对其执行一些操作。我不知道为什么每次执行程序时sharedVar的结果都不一样

public class Main
{
    public static int sharedVar = 0;
    public static void main(String[] args) 
    {
        MyThread mt1 = new MyThread();
        MyThread mt2 = new MyThread();
        mt1.start();
        mt2.start();

        try
        {
            // wait for the threads
            mt1.join();
            mt2.join();
        }
        catch (InterruptedException e1)
        {
            e1.printStackTrace();
        }

        System.out.println(sharedInt); // I expect this value to be 20000, but it's not
    }
}
下面是“MyThread”类

公共类MyThread扩展线程
{
私人整数倍=10000;
私有同步的void addOne()
{
对于(int i=0;i
sharedVar的最终结果有时是13735、12508或18793;但从来没有,这是我期待的结果。该程序的另一个有趣之处是,时间=1000。我的最终成绩总是2000分


有人能解释这种现象吗?

当您在非静态方法上使用
同步时,您使用当前对象作为监视器


在静态方法上使用
synchronized
时,使用类的当前对象(
ClassName.class
static字段)作为监视器

在本例中,您对线程的对象(两个不同的实例)使用了
synchronized
,因此两个不同的线程将同时修改您的
sharedVar
静态字段

你可以用不同的方法修复它

addOne
方法移动到
Main
并使其
static

private static synchronized void addOne(int times)
{
    for (int i = 0; i < times; ++i)
    {
        sharedVar++;
    }
}

但是,如果您只需要在多个线程中更改一个整数,则可以使用
java.til.concurrent.
中的类,如
AtomicLong
AtomicInteger
同步方法保护资源
这意味着您的代码相当于:

private void addOne()
{
    synchronized(this)
    {
        for (int i = 0; i < times; ++i)
        {
            Main.sharedVar ++;
        }
    }
}

您将观察到预期的行为。正如下面的评论所建议的,使用不同的对象进行同步比使用MyThread.class
更好,因为类对象可以从多个点访问,并且其他代码可以很容易地尝试使用相同的对象进行同步。

sharedVar
定义为一个
原子长
,而不是
int
。使函数
同步
也可以工作,但效率较低,因为您只需要同步增量。

当线程即将执行“同步实例方法时,它会取消对对象的锁定(确切地说,锁定该对象监视器)

因此,在您的例子中,线程mt1获取对象mt1上的锁,线程mt2获取对象mt2上的锁,它们不会相互阻止,因为两个线程正在处理两个不同的锁

当两个线程同时修改一个共享变量(非同步方式)时,结果是不可预测的

关于值1000的情况,对于较小的输入,交错执行可能会产生正确的结果(幸运的是)


Sol:从addOne方法中删除synchronized关键字,并将sharedVal设置为“AtomicInteger

在启动方法后立即加入线程。从这个线程开始,线程-1将启动并进入死状态,然后线程-2将启动并进入死状态。因此,它将始终打印您的预期输出

更改代码,如下所示:-

public class Main{

    public static int sharedVar = 0;

    public static void main(String[] args)

        {
            MyThread mt1 = new MyThread();
            MyThread mt2 = new MyThread();

            try

                {
                    mt1.start();
                    mt1.join();
                    mt2.start();
                    mt2.join();
                }

            catch (InterruptedException e1)

                {
                    e1.printStackTrace();
                }

            System.out.println(sharedVar);

        }
}

class MyThread extends Thread
{
    private int times = 1000000;

    private synchronized void addOne()
        {
            for (int i = 0; i < times; ++i)
                {
                    Main.sharedVar++;
                }
        }

    @Override
    public void run()
        {
            addOne();
        }
}
公共类主{
公共静态int sharedVar=0;
公共静态void main(字符串[]args)
{
MyThread mt1=新的MyThread();
MyThread mt2=新的MyThread();
尝试
{
mt1.start();
mt1.join();
mt2.start();
mt2.join();
}
捕捉(中断异常e1)
{
e1.printStackTrace();
}
System.out.println(sharedVar);
}
}
类MyThread扩展线程
{
私有整数倍=1000000;
私有同步的void addOne()
{
对于(int i=0;i
尝试将sharedVar设置为volatile。将volatile添加到sharedVar后,结果接近20000,但仍低于20000。volatile单独不起作用的原因是
Main.sharedVar++
是一个复合操作(读增量写)。Volatile对此不提供任何保护。我明白了,所以AtomicLong可以工作。如果您的代码可以这样工作,我想指出的是,它可能仍然不是您想要的。然后,代码将确保首先,一个线程执行10000个增量,然后另一个线程执行10000个增量。它不会让两个线程交替递增(由调度程序决定,多多少少)。。使用其他对象作为锁+1总之:)@TheLostMind是的,你是对的,最好锁定其他对象,但我想写一些草稿代码,其中一个公共对象用作同步资源。我不知道为什么“synchronized(MyThread.class)”和“synchronized(Main.class)”都是work.@lyen-它们之所以有效,是因为两个类只有一个类对象。@SillyFreak-我的意思是我们应该在多线程环境中使用
AtomicInteger
而不是
Integer
。接下来,如果使用
Integer
作为锁,则不要增加它。:)
private void addOne()
{
    synchronized(this)
    {
        for (int i = 0; i < times; ++i)
        {
            Main.sharedVar ++;
        }
    }
}
private void addOne()
{
    synchronized(MyThread.class)
    {
        for (int i = 0; i < times; ++i)
        {
            Main.sharedVar ++;
        }
    }
}
public class Main{

    public static int sharedVar = 0;

    public static void main(String[] args)

        {
            MyThread mt1 = new MyThread();
            MyThread mt2 = new MyThread();

            try

                {
                    mt1.start();
                    mt1.join();
                    mt2.start();
                    mt2.join();
                }

            catch (InterruptedException e1)

                {
                    e1.printStackTrace();
                }

            System.out.println(sharedVar);

        }
}

class MyThread extends Thread
{
    private int times = 1000000;

    private synchronized void addOne()
        {
            for (int i = 0; i < times; ++i)
                {
                    Main.sharedVar++;
                }
        }

    @Override
    public void run()
        {
            addOne();
        }
}