Java 是否应该同步run方法?为什么?

Java 是否应该同步run方法?为什么?,java,multithreading,synchronization,synchronized,runnable,Java,Multithreading,Synchronization,Synchronized,Runnable,我一直认为在实现Runnable的java类中同步run方法是多余的我正在试图找出人们为什么这样做: public class ThreadedClass implements Runnable{ //other stuff public synchronized void run(){ while(true) //do some stuff in a thread } } } 这似乎是多余和不必要的,因为他们正

我一直认为在实现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方法吗?

使用
synchronized void blah()
优于
void blah(){synchronized(这个){
这就是您生成的字节码将短1字节,因为同步将是方法签名的一部分,而不是操作本身。这可能会影响JIT编译器内联方法的机会。除此之外,没有区别

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) {}
  }
}
}
最好的选择是使用内部
private final Object lock=new Object()

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) {}
  }
}
}
所以我想说不,不要在签名中使用
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。有人试图执行一个Adt,它迫使后备数组的大小调整。当它们在调整数组的大小时,有人调用一个<代码> AuffExcToStand(5)。

在另一个线程上。现在突然你试图访问
数据[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;
    }
}
类堆栈{
int[]数据=新的int[10];
int pos=0;
无效添加(国际公司){
if(pos==data.length){
int[]tmp=新int[pos*2];
对于(inti=0;i
理论上你可以毫无问题地调用run方法本身(毕竟它是公共的)。但这并不意味着你应该这么做。所以基本上没有理由这么做,除了给调用run()的线程增加微不足道的开销之外除非您多次使用实例调用
新线程
——尽管我不确定线程API是否合法,而且b)似乎完全没有用


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

为什么?额外的安全性最低,而且我看不到任何可能产生影响的情况

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) {}
  }
}
}

为什么不呢?这不是标准的。如果你作为团队的一部分进行编码,当其他成员看到你同步的
运行时,他可能会花费30分钟来试图弄清楚你的
运行或你用来运行
可运行
的框架有什么特别之处。根据我的经验,这是没有用的ful添加“synchronized”关键字到run()方法。如果我们需要同步多个线程,或者我们需要一个线程安全队列,我们可以使用更合适的组件,例如ConcurrentLinkedQueue。

实际上,很容易证明“同步还是不同步”

如果方法调用可以改变对象的内部状态,则不需要“同步”

简单例子

public class Counter {

  private int count = 0; 

  public void incr() {
    count++;
  }

  public int getCount() {
    return count;
  }
}
在上面的示例中,incr()需要同步,因为它将更改count的val,而getCount()同步是不必要的

然而,还有另一种情况,如果计数是java.lang.Long,Double,Object,那么您需要声明为

private volatile long count = 0;
确保ref更新是原子的


基本上,在处理多线程时,99%的时间都需要考虑这一点。

同步
运行(
Runnable
的方法是完全没有意义的,除非你想在多个线程之间共享
Runnable
,并且你想将这些线程的执行顺序化。这在术语上基本上是矛盾的


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

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

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) {}
  }
}
}
Kat类{
公共静态void main(字符串…参数){
螺纹t1;
//MyUsualRunnable是常用的东西,只有这样才允许并发
MyUsualRunnable m0=新的MyUsualRunnable();
对于(int i=0;i<5;i++){
t1=新线程(m0);//*imp*此处创建的所有线程都传递相同的可运行线程