为什么我们需要一个静态锁来同步system.out.println()?

时间:2012-06-27 23:31:15

标签: java multithreading synchronization scjp

我正在学习java认证,我从Mughal的书中看到了这个例子:

public class Smiley extends Thread
{
    @Override
    public void run()
    { 
        while(true)
        { 
            synchronized(this)
            {
                try
                { 
                    System.out.print(":"); 
                    Thread.sleep(100); 
                    System.out.print("-"); 
                    Thread.sleep(100); 
                    System.out.println(")"); 
                    Thread.sleep(100); 
                }
                catch(InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }


    public static void main(String[] args)
    {
        new Smiley().start();
        new Smiley().start();
    }
}

目的是每行打印一个笑脸:-)。我的问题是,为什么同步实例(这个)不能实现这一点?为什么我们需要在静态级别上进行同步?

谢谢,

3 个答案:

答案 0 :(得分:6)

因为请注意main()函数会创建两个 Smiley类。他们每个人都在自己的线程上运行。由于它们锁定this,它们都将立即获得锁定,而不会与另一个线程发生争用。在这种情况下,synchronize(this)的锁定方案完成任何事情。

在处理多线程问题时,您必须考虑“我想保护什么?”在这种情况下,您需要保护System.out,以确保您正在访问它按照你想要的顺序。由于System.out是静态的,因此您需要某种外部作用域锁定,每个线程必须先获取它们才能写入。

您可以使用ReentrantLock来实现此目标。

答案 1 :(得分:2)

请不要使用synchronized(this) - 这一般是不好的做法 如上所述 - 锁应该在两个线程之间共享,在这种情况下,锁是每个类的实例(即 - 由新的Smiley创建的对象)。
你应该拥有的是一个共享锁,可能是使用一个静态变量,它在同一个类的所有实例之间共享,
或者将锁作为参数传递给笑脸的CTOR。
我将根据@Jonathon Reinhart使用重入锁的建议为第二个选项提供示例

public class Smiley extends Thread
{
   private  ReentantLock lock;

   public Smiley(ReentrantLock lock) { 
      this.lock = lock;
   }

   @Override
   public void run()
   { 
     while(true)
     { 
        try {
           lock.lock();
           System.out.print(":"); 
           Thread.sleep(100); 
           System.out.print("-"); 
           Thread.sleep(100); 
           System.out.println(")"); 
           Thread.sleep(100); 
        }
        catch(InterruptedException e) {
                e.printStackTrace();
        }
        finally {  
           lock.unlock();
        }
     } 
  }

}


public static void main(String[] args)
{
    ReentrantLock lock = new ReentantLock();
    new Smiley(lock).start();
    new Smiley(lock).start();
}

一些指示 -
一个。请记住,解锁代码必须在finally子句中 - 这是一个很好的做法(你也可以尝试阻止,最后阻塞,没有catch块)。
湾您可以考虑使用java.util.concurrent包中的其他锁替换ReentrantLock - 根据您的需要

答案 2 :(得分:1)

这实际上是你要问的两个问题,答案是:

  • 为什么在实例上进行同步(this)无法实现此目的?

因为您正在获取两个不同的隐式锁,所以允许同步块内的指令由两个线程同时执行,并且实际上可能是交错的。

  • 为什么我们需要在静态级别进行同步?

您无需在静态级别进行同步。您需要在线程共享的对象的同一实例上进行同步。

实现所需内容的最简单方法是以下列方式在System.out上进行同步:

@Override
public void run() {
    while (true) {
        synchronized (System.out) {
            try {
                System.out.print(":");
                Thread.sleep(100);
                System.out.print("-");
                Thread.sleep(100);
                System.out.println(")");
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}