【问题标题】:Synchronized doesn't help to achieve mutual exclusion同步无助于实现互斥
【发布时间】:2014-05-31 14:22:47
【问题描述】:

C类:

class C extends Thread
{
    public static int cr;

    C(int n) 
    {
        cr = n;
    }

    public void run()
    {
        go();
    }

    synchronized void go()
    {
        for (int i = 0; i < 10000; i++)
        {
            cr++;
        }
    }
}

开课

class Launch
{
    public static void main(String args[]) throws InterruptedException
    {
        C[] c = new C[10];

        for (int i = 0; i < 10; i++)
        {
            c[i] = new C(0);
        }

        for (int i = 0; i < 10; i++)
        {
            c[i].start();
        }

        System.out.println(C.spaces);
    }
}

它给我的不是 100,000,而是低于 100k 的数字。为什么?我做了go()同步的方法,所以它一次只能被一个线程使用..?我错过了什么?

【问题讨论】:

  • 什么是同步on

标签: java multithreading thread-safety


【解决方案1】:

synchronized void go(){...} 表示它在当前实例(this)上同步。由于此方法属于您的自定义 Thread 类并且您正在创建 10 个线程,因此存在 10 个不同的 this 引用。

这是首选不扩展 Thread 类,而是实现Runnable 接口并将此接口的实例传递给任意数量的线程的原因之一。

另一个问题是您在不等待线程完成的情况下打印已编辑的值。

您需要创建一个实例,该实例将保存您想要更改的值并仅从 一个实例 调用同步方法,因为默认情况下提到 synchronized void method 是在this(调用此方法的当前对象)上同步。

class MyTask implements Runnable {

    public volatile int counter;

    MyTask(int n) {
        counter = n;
    }

    public void run() {
        System.out.println(Thread.currentThread().getName()+" entered run");
        go();
        System.out.println(Thread.currentThread().getName()+" finished run");
    }

    synchronized void go() {

        System.out.println(Thread.currentThread().getName()+" entered go");
        for (int i = 0; i < 10000; i++) {
            counter++;
        }
        System.out.println(Thread.currentThread().getName()+" left from go");
    }
}

class Luncher {
    public static void main(String args[]) throws InterruptedException {

        //lets create task we want to execute in parallel
        MyTask task = new MyTask(0);

        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++)//create thread instances
            threads[i] = new Thread(task);

        for (int i = 0; i < 10; i++)//start threads
            threads[i].start();

        for (int i = 0; i < 10; i++)
            threads[i].join();//hold main thread to wait till all threads will finish
        System.out.println(task.counter);
    }
}

【讨论】:

  • 它没有给我 100,000 因为主线程没有等待其他线程完成。
  • @Braj 我知道。这是我在回答中提到的另一个问题。
  • @Pshemo Beautiful,非常感谢,我想我现在明白了!
  • @Pshemo 虽然我不确定如果我们只有一个对象任务它是如何工作的......我们如何在同一个对象上拥有 10 个不同的线程?
  • 好的,让我们看看你迷路的地方。你知道synchronized void foo(){...}void foo(){ synchronized(this){...} } 实际上是一样的吗?
【解决方案2】:

 c[i] = new C(0);

你每次都在创建新的 c 实例

 synchronized void go()
{
    for (int i = 0; i < 10000; i++)
    {
        cr++;
    }
}

你总是会得到小于 10000 的数字,我不知道你为什么期望 100,000

i

回答你的最后一个问题

I made method go() synchronized, so it should be used by only one thread at a time..?

如果一次只有一个线程,则不需要同步(多线程概念)。

【讨论】:

    【解决方案3】:

    它给我的不是 100,000,而是低于 100k 的数字。为什么?

    一切正常,但main 线程不等待其他线程完成计算,因此main 线程中的输出是随机的。

    使用Thread#join(),这样主线程在执行主线程的最后一行之前等待所有其他线程死亡。

    for (int i = 0; i < 10; i++) {
        c[i].start();
        c[i].join(); // Waits for this thread to die
    }
    
    System.out.println(C.cr); // output 100000
    

    值得一读How do I pause main() until all other threads have died?

    【讨论】:

    • 非常感谢。但是现在即使没有同步,我也能得到 100,000... :( 我什么都不懂... 编辑:对不起,我应该加入另一个循环。现在一切正常,再次感谢!edit2: 实际上没有,如果我加入了另一个循环,即使 go 是同步的,它也不会给我 100k :(
    • 同步不是问题。问题在于在等待其他线程完成计算之前打印值的main 线程。它可能会帮助你How to make the main end last?
    • 我为 (int i = 0; i
    • @Ijustwant2learn 如果线程A 在线程B 上调用join,那么A 将等待B 完成。在这种情况下,主线程运行c[i].start() 并立即运行c[i].join(),这意味着在它迭代到下一个线程c[i+1] 并启动它之前,它将等待c[i] 如此有效地完成,你这里没有并行性,因为只有一个线程一次就可以了。
    • @Pshemo 看看我上面的评论,我把 join 和 start 分开成不同的循环。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-10
    • 2014-05-02
    • 1970-01-01
    • 2012-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多