您应该同步 run 方法吗?为什么或为什么不呢?

发布于 2024-12-02 19:59:20 字数 839 浏览 1 评论 0原文

我一直认为在实现Runnable的java类中同步run方法是多余的。 我试图弄清楚人们为什么这样做:

public class ThreadedClass implements Runnable{
    //other stuff
    public synchronized void run(){
        while(true)
             //do some stuff in a thread
        }
    }
}

这似乎是多余和不必要的,因为他们正在为另一个线程获取对象的锁。或者更确切地说,他们明确表示只有一个线程可以访问 run() 方法。但既然是run方法,那它本身不就是自己的线程吗?因此,只有它自己可以访问它不需要单独的锁定机制?

我在网上发现了一个建议,通过同步 run 方法,您可以通过这样做创建一个事实上的线程队列:

 public void createThreadQueue(){
    ThreadedClass a = new ThreadedClass();
    new Thread(a, "First one").start();
    new Thread(a, "Second one, waiting on the first one").start();
    new Thread(a, "Third one, waiting on the other two...").start();
 }

我个人永远不会这样做,但这引出了一个问题:为什么有人会同步 run 方法。 有什么想法为什么或为什么不应该同步 run 方法吗?

I have always thought that synchronizing the run method in a java class which implements Runnable is redundant. I am trying to figure out why people do this:

public class ThreadedClass implements Runnable{
    //other stuff
    public synchronized void run(){
        while(true)
             //do some stuff in a thread
        }
    }
}

It seems redundant and unnecessary since they are obtaining the object's lock for another thread. Or rather, they are making explicit that only one thread has access to the run() method. But since its the run method, isn't it itself its own thread? Therefore, only it can access itself and it doesn't need a separate locking mechanism?

I found a suggestion online that by synchronizing the run method you could potentially create a de-facto thread queue for instance by doing this:

 public void createThreadQueue(){
    ThreadedClass a = new ThreadedClass();
    new Thread(a, "First one").start();
    new Thread(a, "Second one, waiting on the first one").start();
    new Thread(a, "Third one, waiting on the other two...").start();
 }

I would never do that personally, but it lends to the question of why anyone would synchronize the run method. Any ideas why or why not one should synchronize the run method?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

爱她像谁 2024-12-09 19:59:20

同步 Runnablerun() 方法是完全没有意义的,除非您想在多个线程之间共享 Runnable 并且您希望按顺序执行这些线程。这基本上是一个矛盾。

理论上还有另一种更复杂的场景,您可能希望同步 run() 方法,这同样涉及在多个线程之间共享 Runnable,但也利用wait()notify()。我在使用 Java 21 年多的时间里从未遇到过它。

Synchronizing the run() method of a Runnable is completely pointless unless you want to share the Runnable among multiple threads and you want to sequentialize the execution of those threads. Which is basically a contradiction in terms.

There is in theory another much more complicated scenario in which you might want to synchronize the run() method, which again involves sharing the Runnable among multiple threads but also makes use of wait() and notify(). I've never encountered it in 21+ years of Java.

没有心的人 2024-12-09 19:59:20

void blah() { synchronized(this) { 相比,使用 synchronized void blah() 有 1 个优点,即生成的字节码将短 1 个字节,因为同步将成为方法签名的一部分,而不是单独的操作。这可能会影响 JIT 编译器内联该方法的机会。除此之外没有什么区别。

最好的选择是使用内部 private final Object lock = new Object() 来防止有人可能锁定您的监视器。它实现了相同的结果,而没有邪恶的外部锁定的缺点。你确实有那个额外的字节,但它很少有什么区别。

所以我想说不,不要在签名中使用 synchronized 关键字。相反,请使用类似

public class ThreadedClass implements Runnable{
    private final Object lock = new Object();

    public void run(){
        synchronized(lock) {
            while(true)
                 //do some stuff in a thread
            }
        }
    }
}

“编辑”的内容来响应评论:

考虑同步的作用:它防止其他线程进入相同的代码块。想象一下您有一个像下面这样的课程。假设当前大小为 10。有人尝试执行添加操作,它会强制调整后备数组的大小。当他们正在调整数组大小时,有人在不同的线程上调用 makeExactSize(5)。现在,您突然尝试访问data[6],结果却突然崩溃了。同步应该可以防止这种情况发生。在多线程程序中,您只需要同步。

class Stack {
    int[] data = new int[10];
    int pos = 0;

    void add(int inc) {
        if(pos == data.length) {
            int[] tmp = new int[pos*2];
            for(int i = 0; i < pos; i++) tmp[i] = data[i];
            data = tmp;
        }
        data[pos++] = inc;
    }

    int remove() {
        return data[pos--];
    }

    void makeExactSize(int size) {
        int[] tmp = new int[size];
        for(int i = 0; i < size; i++) tmp[i] = data[i];
        data = tmp;
    }
}

There is 1 advantage to using synchronized void blah() over void blah() { synchronized(this) { and that is your resulting bytecode will be 1 byte shorter, since the synchronization will be part of the method signature instead of an operation by itself. This may influence the chance to inline the method by the JIT compiler. Other than that there is no difference.

The best option is to use an internal private final Object lock = new Object() to prevent someone from potentially locking your monitor. It achieves the same result without the downside of the evil outside locking. You do have that extra byte, but it rarely makes a difference.

So I would say no, don't use the synchronized keyword in the signature. Instead, use something like

public class ThreadedClass implements Runnable{
    private final Object lock = new Object();

    public void run(){
        synchronized(lock) {
            while(true)
                 //do some stuff in a thread
            }
        }
    }
}

Edit in response to comment:

Consider what synchronization does: it prevents other threads from entering the same code block. So imagine you have a class like the one below. Let's say the current size is 10. Someone tries to perform an add and it forces a resize of the backing array. While they're in the middle of resizing the array, someone calls a makeExactSize(5) on a different thread. Now all of a sudden you're trying to access data[6] and it bombs out on you. Synchronization is supposed to prevent that from happening. In multithreaded programs you simply NEED synchronization.

class Stack {
    int[] data = new int[10];
    int pos = 0;

    void add(int inc) {
        if(pos == data.length) {
            int[] tmp = new int[pos*2];
            for(int i = 0; i < pos; i++) tmp[i] = data[i];
            data = tmp;
        }
        data[pos++] = inc;
    }

    int remove() {
        return data[pos--];
    }

    void makeExactSize(int size) {
        int[] tmp = new int[size];
        for(int i = 0; i < size; i++) tmp[i] = data[i];
        data = tmp;
    }
}
风筝有风,海豚有海 2024-12-09 19:59:20

为什么?最低限度的额外安全性,我看不出任何可能产生影响的情况。

为什么不呢?这不是标准的。如果您作为团队的一员进行编码,当其他成员看到您同步的 run 时,他可能会浪费 30 分钟试图弄清楚您的 run 有什么特别之处> 或使用您用来运行 Runnable 的框架。

Why? Minimal extra safety and I don't see any plausible scenario where it would make a difference.

Why not? It's not standard. If you are coding as part of a team, when some other member sees your synchronized run he'll probably waste 30 minutes trying to figure out what is so special either with your run or with the framework you are using to run the Runnable's.

东北女汉子 2024-12-09 19:59:20

根据我的经验,向 run() 方法添加“synchronized”关键字没有用。如果我们需要同步多个线程,或者需要线程安全的队列,我们​​可以使用更合适的组件,例如ConcurrentLinkedQueue。

From my experience, it's not useful to add "synchronized" keyword to run() method. If we need synchronize multiple threads, or we need a thread-safe queue, we can use more appropriate components, such as ConcurrentLinkedQueue.

丢了幸福的猪 2024-12-09 19:59:20

查看代码注释并取消注释并运行不同的块以清楚地看到差异,注意只有使用相同的可运行实例时同步才会有差异,如果每个启动的线程都获得一个新的可运行实例,则不会有任何差异。

class Kat{

public static void main(String... args){
  Thread t1;
  // MyUsualRunnable is usual stuff, only this will allow concurrency
  MyUsualRunnable m0 = new MyUsualRunnable();
  for(int i = 0; i < 5; i++){
  t1 = new Thread(m0);//*imp*  here all threads created are passed the same runnable instance
  t1.start();
  }

  // run() method is synchronized , concurrency killed
  // uncomment below block and run to see the difference

  MySynchRunnable1 m1 = new MySynchRunnable1();
  for(int i = 0; i < 5; i++){
  t1 = new Thread(m1);//*imp*  here all threads created are passed the same runnable instance, m1
  // if new insances of runnable above were created for each loop then synchronizing will have no effect

  t1.start();
}

  // run() method has synchronized block which lock on runnable instance , concurrency killed
  // uncomment below block and run to see the difference
  /*
  MySynchRunnable2 m2 = new MySynchRunnable2();
  for(int i = 0; i < 5; i++){
  // if new insances of runnable above were created for each loop then synchronizing will have no effect
  t1 = new Thread(m2);//*imp*  here all threads created are passed the same runnable instance, m2
  t1.start();
}*/

}
}

class MyUsualRunnable implements Runnable{
  @Override
  public void  run(){
    try {Thread.sleep(1000);} catch (InterruptedException e) {}
}
}

class MySynchRunnable1 implements Runnable{
  // this is implicit synchronization
  //on the runnable instance as the run()
  // method is synchronized
  @Override
  public synchronized void  run(){
    try {Thread.sleep(1000);} catch (InterruptedException e) {}
}
}

class MySynchRunnable2 implements Runnable{
  // this is explicit synchronization
  //on the runnable instance
  //inside the synchronized block
  // MySynchRunnable2 is totally equivalent to MySynchRunnable1
  // usually we never synchronize on this or synchronize the run() method
  @Override
  public void  run(){
    synchronized(this){
    try {Thread.sleep(1000);} catch (InterruptedException e) {}
  }
}
}

Go through the code comments and uncomment and run the different blocks to clearly see the difference, note synchronization will have a difference only if the same runnable instance is used, if each thread started gets a new runnable it won't make any difference.

class Kat{

public static void main(String... args){
  Thread t1;
  // MyUsualRunnable is usual stuff, only this will allow concurrency
  MyUsualRunnable m0 = new MyUsualRunnable();
  for(int i = 0; i < 5; i++){
  t1 = new Thread(m0);//*imp*  here all threads created are passed the same runnable instance
  t1.start();
  }

  // run() method is synchronized , concurrency killed
  // uncomment below block and run to see the difference

  MySynchRunnable1 m1 = new MySynchRunnable1();
  for(int i = 0; i < 5; i++){
  t1 = new Thread(m1);//*imp*  here all threads created are passed the same runnable instance, m1
  // if new insances of runnable above were created for each loop then synchronizing will have no effect

  t1.start();
}

  // run() method has synchronized block which lock on runnable instance , concurrency killed
  // uncomment below block and run to see the difference
  /*
  MySynchRunnable2 m2 = new MySynchRunnable2();
  for(int i = 0; i < 5; i++){
  // if new insances of runnable above were created for each loop then synchronizing will have no effect
  t1 = new Thread(m2);//*imp*  here all threads created are passed the same runnable instance, m2
  t1.start();
}*/

}
}

class MyUsualRunnable implements Runnable{
  @Override
  public void  run(){
    try {Thread.sleep(1000);} catch (InterruptedException e) {}
}
}

class MySynchRunnable1 implements Runnable{
  // this is implicit synchronization
  //on the runnable instance as the run()
  // method is synchronized
  @Override
  public synchronized void  run(){
    try {Thread.sleep(1000);} catch (InterruptedException e) {}
}
}

class MySynchRunnable2 implements Runnable{
  // this is explicit synchronization
  //on the runnable instance
  //inside the synchronized block
  // MySynchRunnable2 is totally equivalent to MySynchRunnable1
  // usually we never synchronize on this or synchronize the run() method
  @Override
  public void  run(){
    synchronized(this){
    try {Thread.sleep(1000);} catch (InterruptedException e) {}
  }
}
}
烧了回忆取暖 2024-12-09 19:59:20

理论上,您可以毫无问题地调用 run 方法本身(毕竟它是公共的)。但这并不意味着人们应该这样做。所以基本上没有理由这样做,除了给调用 run() 的线程增加了可以忽略不计的开销。好吧,除非您多次使用实例调用 new Thread - 尽管我 a) 不确定这对于线程 API 是否合法,b) 似乎完全没用。

此外,您的 createThreadQueue 也不起作用。非静态方法上的 synchronized 在实例对象(即 this)上同步,因此所有三个线程都将并行运行。

Well you could theoretically call the run method itself without problem (after all it is public). But that doesn't mean one should do it. So basically there's no reason to do this, apart from adding negligible overhead to the thread calling run(). Well except if you use the instance multiple times calling new Thread - although I'm a) not sure that's legal with the threading API and b) seems completely useless.

Also your createThreadQueue doesn't work. synchronized on a non-static method synchronizes on the instance object (ie this), so all three threads will run in parallel.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文