多线程同步问题

时间:2014-08-25 16:58:15

标签: java multithreading synchronization

我有这样的代码:

public class OtherClass {

    // OtherClass
    public synchronized static void firstMethod() {
        System.out.println("FIRST METHOD");

    }

    public synchronized static void secondMethod() {
        System.out.println("SECOND METHOD");
        // In actual code I would have try catch for this but here I just didn't
        // include it
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

public class MainClass {

    // main method of MainClass
    public static void main(String args[]) {
        Thread firstThread = new Thread() {
            public void run() {
                while (true) {
                    OtherClass.firstMethod();
                }

            }
        };

        Thread secondThread = new Thread() {
            public void run() {
                while (true) {
                    OtherClass.secondMethod();
                }

            }
        };

        secondThread.start();
        firstThread.start();

    }
}

我首先启动第二个线程的原因是因为我希望首先执行secondMethod OtherClass。我应该每隔5秒在控制台输出中查看"FIRST METHOD""SECOND METHOD"。原因是因为Thread.sleep没有放弃锁,5秒钟第一个线程无法访问第一个方法,因为第二个线程已经锁定了类,而它在第二个方法中告诉线程睡5秒钟。但是我得到了非常意外的结果。我在控制台输出中获得的所有内容都是每5秒"SECOND METHOD"firstMethod未被调用。

4 个答案:

答案 0 :(得分:4)

忽略代码中的编译问题。

这只是巧合。线程调度程序只是决定继续执行第二个线程,该第二个线程过快地重新获取锁定。

运行它足够长(或者使用较短的睡眠时间以获得更快的结果)并且您将看到调用其他方法。

答案 1 :(得分:2)

  

我首先启动第二个线程的原因是因为我希望首先执行OtherClass的secondMethod。

这不是线程的工作方式。这不是线程的用途。除了在它们之间提供显式同步之外,线程提供 no 保证之前发生的事情。一般来说,使用的同步越多,拥有多个线程的好处就越少。

在您的示例中,您具有显式同步,可以阻止firstMethod()和secondMethod()的任何并发执行,但是您没有任何保证首先运行哪个,以及哪个将运行第二个。有可能,main()将在它们中的任何一个运行之前终止。此时,由调度程序决定选择哪一个将在何时运行。不要求它以与您的代码称为start()方法相同的顺序启动它们。

您的示例可能具有教育意义,但它也是何时使用线程的示例。你的同步非常繁重。你的程序基本上做了两件事,firstMethod()和secondMethod(),并且同步确保它们不能重叠。在生产软件中,如果您有两个必须不重叠的任务,那么如果它们总是由同一个线程执行,那么它将简化您的程序逻辑。

  

我进入控制台输出的所有内容都是"第二种方法"每5秒钟。第一种方法没有被召唤。

我的问题在我看到之前就已经过编辑,因此我不知道您是在谈论原始版本还是固定版本。但无论如何:

程序中的同步确实保证两个线程轮流。它只是阻止它们同时打印。

你的每个线程都运行一个循环来抓取锁,打印东西,释放锁,然后立即尝试再次抓住锁。当一个正在运行的线程释放一个锁,然后立即尝试再次获取它时,它有可能成功。一些其他线程正在等待锁定并不重要。操作系统不知道你的程序想要完成什么,但 知道它可以通过让线程继续运行而不是阻塞它来更有效地使用CPU阻止一些其他线程。

你需要使用一些额外的同步来让线程轮流,但正如我所说,在实际程序中,你使用的同步越多,使用线程的好处就越少。

答案 2 :(得分:0)

您的处理器不会同时执行您的主题;每次在你的第一个线程上运行你的第二个线程。

行为很明确:您的处理器执行您的第二个线程。然后,处理器执行您的第一个线程,并看到它被第二个线程锁定。 5秒后,再次调用第二个线程。它产生输出,释放锁并再次锁定它。如果再次调用第一个线程,它将再次被锁定。

要解决此问题,请在结束时添加Thread.yield()。这将使处理器在继续执行第二个线程之前调用第一个线程(第一个线程不是唯一被调用的线程,它只是将第二个线程从其执行中移除一次)。然后你的第一个线程获得锁定,等待5秒,输出并调用Thread.yield();然后你的第二个线程再次获得锁定,依此类推。

答案 3 :(得分:0)

您遇到的是线程饥饿。在您的情况下,一个线程被无限期地阻塞,等待进入同步块,因为另一个线程一直被允许访问。

要克服线程饥饿,您需要采用某种公平锁定。被设想为公平的锁优先于等待最长时间获取所述锁的线程。这种锁定策略消除了线程不足的可能性,并确保所有等待线程将在某个时间执行。

使用公平参数设置为true的ReentrantLock可以轻松实现Java中的公平锁定。