【问题标题】:java:inefficiency of synchronized methods [duplicate]java:同步方法的效率低下[重复]
【发布时间】:2017-09-26 00:42:13
【问题描述】:

我正在通过这个程序,但无法理解它。

    class Q
  {
     int n;
    synchronized int get()
   { 
    System.out.println("Got: " + n); 
    return n;
   }
    synchronized void put(int n)
   { 
   this.n = n; 
   System.out.println("Put: " + n);
 } 
}
  class Producer implements Runnable 
{ 
Q q;
Producer(Q q) 
{
 this.q = q;
 new Thread(this, "Producer").start(); }
public void run() 
{
 int i = 0;
 while(true) 
{ q.put(i++); }
}
}
class Consumer implements Runnable 
{
 Q q;
Consumer(Q q)
 { 
 this.q = q; new Thread(this, "Consumer").start();
 }
public void run() { while(true) { q.get(); } }
}
class PC 
{
public static void main(String args[]) 

{ 
     Q q = new Q(); 
     new Producer(q); 
      new Consumer(q);

System.out.println("Press Control-C to stop.");
}
}

虽然 Q 上的 put( ) 和 get( ) 方法是同步的,但没有什么能阻止生产者超越消费者,也不会阻止消费者两次消费相同的队列值。因此,您会得到此处显示的错误输出:

放置:1 得到:1 得到:1 得到:1 得到:1 得到:1 放: 2 放: 3 放: 4 放: 5 放: 6 放: 7 得到:7

如您所见,生产者放1后,消费者开始连续五次得到相同的1。然后,生产者恢复并生产2到7,而没有让消费者有机会消费它们。我的问题是为什么我们会得到如此奇怪的输出,我的意思是当 get() 被执行时它返回一个值,然后线程应该从监视器中退出,而不是它停留在那里并打印 1 这么多次并且;然后 put() ,它如何能够增加和打印这些值。 ???请帮忙,我知道我可能会显得幼稚,因为我是 java 的新手。

【问题讨论】:

  • 我看不出输出有什么特别奇怪的地方。这就是多线程的事情。仅仅因为一个线程放弃了监视器,并不意味着另一个线程可以立即运行。调度程序可能会在第二个线程有机会之前让第一个线程一遍又一遍地拥有监视器。
  • “低效率”从何而来。与什么相比效率低下?
  • 为什么要重新实现AtomicInteger
  • 当你修复你不相关的标题时,请格式化这个烂摊子。我无法理解你的问题。

标签: java multithreading synchronization synchronized thread-synchronization


【解决方案1】:

操作系统调度器决定哪个线程执行和哪个获得锁。它不必在线程之间交替针锋相对,它只是确保没有任何东西会因为缺少 cpu 切片而饿死。所以你可以看到一个线程运行了一段时间,然后另一个有机会。让线程拥有一段时间的先行权可能有助于最大限度地减少上下文切换。每次调度程序切换正在运行的线程都会产生开销,最好让线程有机会运行并完成某些事情,而不是在它们之间颠簸。

如果您希望线程严格交替工作,那么您可以通过让线程等待直到 Q 处于有效状态来强制执行此操作:

class Q {
    Integer n; // nullable
    public synchronized int get() {
        while (n == null) {
            wait();
        }
        notifyAll();
        System.out.println("got: " + n);
        int retval = n;
        n = null;
        return retval;
    }
    public synchronized void put(int n) {
        while (n != null) {
            wait();
        }
        notifyAll();
        this.n = n;
        System.out.println("put: " + n);
    }
}

这个版本的Q会导致两个线程轮流。

如果您只希望线程在不交替的情况下放置和设置值,那么使用 AtomicInteger 会更简单。同步是为了组合多个动作,使它们不会被其他线程干扰。

【讨论】:

  • 所以你的意思是说,仅仅因为一个线程放弃了监视器,并不意味着另一个线程可以立即运行。调度程序可能会在第二个线程有机会之前让第一个线程一遍又一遍地拥有监视器,因此我们以这种方式获得输出。
  • @garima:是的,没错。
  • 非常感谢内森。 :)
  • 休斯——最后一个疑问。所有这些值都被放入队列吗??
  • -希望你不介意,需要再澄清一下。当我们使用 wait() 和 notify() 方法,即线程间通信时,持有监视器的线程有责任放弃监视器并明确宣布其他线程可以进入监视器并完成他们的工作,那么他们也将遵循同样的方法。对???
猜你喜欢
  • 2015-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多