0

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

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();
    }
}

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

谢谢,

4

3 回答 3

6

因为请注意 main() 函数创建了两个笑脸类。他们每个人都在自己的线程上运行。由于它们正在锁定this,因此它们都将立即获取锁,而不会与其他线程发生争用。在这种情况下,他们的锁定方案synchronize(this)一无所获。

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

您可以使用ReentrantLock来实现这一点。

于 2012-06-27T23:33:02.330 回答
2

请不要使用同步(this) - 这通常是不好的做法
如上所述 - 锁应该在两个线程之间共享,在这种情况下锁是每个类的实例(即 - 由 new 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 子句中 - 这是一个很好的做法(您也可以使用 try 块和 finally 块,没有 catch 块)。
湾。您可以考虑用 java.util.concurrent 包中的其他锁替换 ReentrantLock - 根据您的需要

于 2012-06-28T05:57:21.423 回答
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();
            }
        }
    }
}
于 2013-11-21T17:53:37.183 回答