【问题标题】:Can we use synchronised for run() method in java?我们可以在 java 中使用同步的 run() 方法吗?
【发布时间】:2012-05-26 14:55:27
【问题描述】:

正如我们所知,没有任何规定可以防止多个线程使用start() 方法调用run() 方法。我确实创建了两个对象m1m2 都调用同一个线程来运行。

我需要确保第一个对象完成 (m1.start) 的执行,方法是在第二个对象执行开始之前调用线程。

我的问题是为什么我不能在我创建的线程(即MyThread1)中使用synchronized 关键字和run() 方法?

我尝试在我创建的线程中使用“同步”来运行()方法,但它给出了任意输出(换句话说,m2 不会等待m1 完成执行)。

您可以在程序的最底部看到我得到的输出。

public class ExtendedThreadDemo {

    public static void main(String[] args) {    
        Mythread1 m1 =new Mythread1();
        Mythread1 m2 =new Mythread1();
        m1.start();
        m2.start();
        System.out.println(" main thread exiting ....");
    }
}

我的线程

public class MyThread1 extends Thread {

    public synchronized void run() {
        for(int i=1; i<5; i++) {
            System.out.println(" inside the mythread-1 i = "+ i);
            System.out.println(" finish ");

            if (i%2 == 0) {
                try {
                    Thread.sleep(1000);
                } catch(InterruptedException e) {
                     System.out.println(" the thread has been interrupted ");
                }
            }
        }
    }
}

输出

main thread exiting ....
inside the mythread-1 i = 1
finish
inside the mythread-1 i = 2
finish 
inside the mythread-1 i = 1
finish 
inside the mythread-1 i = 2
finish 
inside the mythread-1 i = 3
finish 
inside the mythread-1 i = 4
finish 
inside the mythread-1 i = 3
finish 
inside the mythread-1 i = 4
finish 

正如您在i = 2 之后看到的,第二个对象(即m2.start())开始执行。

【问题讨论】:

  • 如果你想让它同步,你为什么需要它在扩展线程的类中?只需将其移动到单独的一个。
  • 同步运行方法有点。什么,你想启动 5 个线程,让它们可以互相等待完成?这与让一个线程连续执行五次run 完全相同,只是开销更大。

标签: java multithreading synchronized


【解决方案1】:

在调用m2.start()之前,先在main中调用m1.join()

创建方法synchronized 只会影响对同一个对象上方法的多次调用; m1m2 是不同的对象,所以添加 synchronized 将没有效果。

【讨论】:

  • 谢谢路易斯。我将不能或不能调用同一个实例,例如:m1.start 和 m1.start...java 不允许多次调用同一个方法
  • 请忽略顶级评论..谢谢路易斯。我将不能或不能在 java ex 中调用相同的线程 start() 实例两次:m1.start 并再次调用 m1.start 。如果我尝试调用两次第二次将抛出 IllegalThreadStateException。如果我同意你的观点,我将无法在我的程序中看到同步工作,因为如上所述,我将在第二次调用时得到 IllegalThreadStateException。我回来找你是因为你说“使方法同步只会影响对同一对象上的方法的调用”。如果你能帮助我理解这个同步的概念,我会很高兴的。
  • 我并不是建议你在同一个线程上调用start 两次。是什么让你产生了这个想法?我是说你应该写m1.start(); m1.join(); m2.start();
  • 谢谢你,Louis...我已经按照你说的执行了,它按预期做的很好。困扰我并使我反复思考的是您的第一个建议“使方法同步只会影响对同一对象上的方法的多次调用”...(“对同一对象上的方法的多次调用...”)。一个线程的方法会被多次调用吗?
  • 不,这就是重点。 run 是唯一的方法,它只会被调用一次。
【解决方案2】:

这段代码sn-p:

public synchronized void run() {
  //code
}

在语义上等价于:

public void run() {
  synchronized(this) {
    //code
  }
}

注意this 引用。这意味着使用相同this 引用(锁定对象)的所有synchronized 块是互斥的。但是,您正在创建两个线程(对象),每个线程都在不同的 this 引用上同步。

有几种方法可以改进您的代码:

只使用一个Runnable并创建两个线程:

public class Mythread1 implements Runnable //...

然后:

Runnable r = new Mythread1();
Thread m1 = new Thread(r);
Mythread1 m2 = new Thread(r);

但这只有在Mythread1 runnable 是 statelss 时才有效,因为它将由两个线程共享。

使用同一个锁实例

将一些任意的lock 对象传递给Mythread1 的两个实例,然后使用:

synchronized(lock) {
  //code
}

从 Java 5 开始,同步代码的方法更加复杂,但这种方法适用于简单的情况。

使用Thread.join()

您可以致电:

m1.start();
m1.join();
m2.start();

这样就可以了。

【讨论】:

  • 作为奖励,您能指出使用这种胎面的任何理由吗? :)
  • @MarkoTopolnik:你的意思是start/join/start() 序列?好吧,一个可能的原因是启动一个任务,在主线程中进行一些处理,等待该任务完成 (join) 并继续执行第二个任务,同时获得前台处理和第一个任务的结果。
  • 但是为什么每次都启动一个新线程,而不是重用刚刚完成任务的线程呢?我想在java.util.concurrent 之前它有点意思,因为它比重用一个后台线程的适当解决方案更容易编码。无论如何,很抱歉这样闯入,我不想点燃火焰:)
  • @MarkoTopolnik:你是绝对正确的。但是 OP 似乎还不了解 synchronized 关键字的工作原理(目前),因此现在引入线程池等更抽象的解决方案可能不是一个好主意(好吧,教坏模式也不好......)
  • 一个轻量级的改进(没有执行器)是使用CyclicBarrier
【解决方案3】:

同步的实例方法只会阻碍对同一个类实例的并发访问。如果 run 方法是静态的,您会看到预期的效果。但我不建议将 run 方法设为静态。实际上,如果要保留其当前功能,则不能。您应该考虑使用 m1.join() 等待 m1 完成。如果您有多个要相互等待的线程,另一种效果更好的方法是使用CyclicBarrierCountDownLatch

【讨论】:

  • 我将不能或不能在 java 中调用相同的线程 start() 实例两次,例如:m1.start 并再次调用 m1.start。如果我尝试调用两次第二次将抛出 IllegalThreadStateException。如果我同意你的观点,我将无法在我的程序中看到同步工作,因为如上所述,我将在第二次调用时得到 IllegalThreadStateException。我将在第二次调用时得到 IllegalThreadStateException。我回来找你是因为你说“同步的实例方法只会阻碍对同一个类实例的并发访问。”谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多