方法同步,但由于非序列化的线程行为,代码产生随机结果

问题描述 投票:2回答:3

这是我的代码:

public class ThreadDemo {

public static void main(String args[]) throws Exception {
    Printer[] printers = new Printer[5];
    printers[0] = new Printer("@base");
    printers[1] = new Printer("#try");
    printers[2] = new Printer("!test");
    printers[3] = new Printer("^hello");
    printers[4] = new Printer("*world");

    for (Printer x : printers) {
        x.start();
    }

    try {
        for (Printer y : printers) {
            y.join();
        }
    } catch (InterruptedException e) {
        System.out.println(e);
    }
  }
}

class Printer extends Thread {
public Printer(String name) {
    super(name);
}

public void run() {
    print();
}

public synchronized void print() {
    for (int i = 0; i < 10; i++) {
        System.out.print(getName().charAt(0));
        try {
            sleep(100);
        } catch (InterruptedException e) {
            System.out.println(e + " occured");
        }
    }
  }
}

它导致了

@^!#**@^!#*#@!^@*#^!#^!*@^*@!#@!#*^@#^!*!@^#*#@*^! 

我的期望是所有符号将根据首先启动的线程序列化为@@@@@ ^^^^^。

调用sleep()会让其他线程继续进行直到当前线程的休眠时间,但我想不应该是同步方法的情况。

java multithreading thread-safety synchronized
3个回答
6
投票

同步在这里不起作用。

该关键字确保您不能并行地在同一对象上调用相同的方法。

您正在不同的对象上调用它,因此即使没有关键字,结果也会相同!

(我宁愿假设您看到的结果实际上是由于在这里使用println()引起的。这是一个“非常慢”的操作,当被超线程快速完成所有其他工作的线程使用时会引入“事实上的”同步。我是试图找到一些关于它的其他信息,但这可能需要更多时间)


1
投票

你的代码或我的问题会说你的期望是方法print在对象级别同步,你正在创建新的线程对象,启动线程并调用此方法。

因此,在这种情况下,每个方法在每个单独的线程对象上同步。为了实现您期望的行为,我们可以使print方法保持静态并查看行为更改。您将获得预期的结果,因为那时,方法printPrinter类锁的单个实例上同步。因此,即使不同的线程实例正在调用此方法,因为对于类只有一个锁,线程执行依次发生。

public static synchronized void print() {
    for (int i = 0; i < 10; i++) {
        System.out.print(Thread.currentThread().getName().charAt(0));
        try {
            sleep(100);
        } catch (InterruptedException e) {
            System.out.println(e + " occured");
        }
    }

}

1
投票

@SolomonSlow - 所以必须纠正为'同步方法'可以同时在同一个对象上调用吗?

关于同步实例方法,您只需要知道一件事。你需要知道这......

synchronized void Foobar() { ... }

...只是编写同步块的快捷方式。

void Foobar() {
    synchronized (this) {
        ...
    }
}

这两个方法声明都完全相同。因此,您知道或了解同步块行为的所有内容也可以应用于同步方法。

关于synchronized块的最重要的事情是,“没有两个线程可以同时在同一个对象上同步。”如果您了解这一点,并且如果您知道同步方法只是同步块的快捷方式,那么您可以回答自己的问题。

© www.soinside.com 2019 - 2024. All rights reserved.