有关synchronized关键字如何与锁和线程饥饿一起使用的问题

时间:2016-09-06 16:30:08

标签: java multithreading asynchronous synchronization synchronized

this java tutorial中,有一些代码显示了解释synchronized关键字使用的示例。我的观点是,为什么我不应该写这样的东西:

public class MsLunch {
    private long c1 = 0;
    private long c2 = 0;
    //private Object lock1 = new Object();
    //private Object lock2 = new Object();

    public void inc1() {
        synchronized(c1) {
            c1++;
        }
    }

    public void inc2() {
         synchronized(c2) {
             c2++;
         }
     }
}

不打扰创建锁定对象?另外,为什么还要实例化锁定对象呢?我不能通过空引用吗?我想我在这里错过了一些东西。

另外,假设我在几个线程访问的同一个类中有两个公共同步方法。这两种方法永远不会同时执行,这是真的吗?如果答案是肯定的,是否有一种内置机制可以阻止一种方法饥饿(与其他方法相比,从未执行或执行过少次数)?

3 个答案:

答案 0 :(得分:3)

首先你不能将原始变量传递给synchronized,它需要一个引用。其次,教程只是一个显示防护块的示例。它不是c1,c2它试图保护它,而是试图保护synchronized块内的所有代码。

JVM使用操作系统的调度算法。

What is the JVM Scheduling algorithm?

因此,查看线程是否缺乏并不是JVM的责任。但是,您可以将线程的优先级指定为优先于其他线程来执行。

  

每个帖子都有优先权。具有较高优先级的线程优先于具有较低优先级的线程执行。每个线程可能也可能不会被标记为守护进程。当在某个线程中运行的代码创建一个新的Thread对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护进程线程。

自:https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html

如果您担心这种情况,那么您必须自己实施。就像维护一个检查饥饿线程的线程一样,随着时间的推移,它增加了等待时间比其他线程更长的线程的优先级。

是的,已经同步的两个方法永远不会同时在同一个实例上执行。

答案 1 :(得分:3)

正如@ 11thdimension所回复,你不能在原始类型上同步(例如,长)。它必须是一个类对象。

所以,你可能想做类似以下的事情:

Long c1 = 0;
public void incC1() {
  synchronized(c1) {
    c1++;
  }
}

这将无法正常工作,因为“c1 ++”是“c1 = c1 + 1”的快捷方式,它实际上将新对象分配给c1,因此,两个线程可能最终位于同一代码块中

要使锁正常工作,不应重新分配正在同步的对象。 (好吧,也许在极少数情况下,你真的知道自己在做什么。)

您无法将null对象传递给synchronized(...)语句。 Java正在有效地在ref'd对象上创建信号量,并使用该信息来防止多个线程访问相同的受保护资源。

您并不总是需要单独的锁定对象,就像同步方法一样。在这种情况下,类对象实例本身用于存储锁定信息,就像在方法iteslf中使用'this'一样:

public void incC1() {
    synchronized(this) {
      c1++;
    }
}

答案 2 :(得分:1)

  

为什么还要实例化锁定对象?我不能只传递一个空引用吗?

正如其他人所提到的,你无法锁定long c1,因为它是一个原始的。 Java锁定在与对象实例关联的监视器上。这就是为什么你也无法锁定null

thread tutorial正试图展示一个好的模式,即创建private final锁定对象以精确控制您尝试保护的互斥锁位置。在synchronized或其他this对象上调用public可能会导致外部呼叫者阻止您的方法,这可能不是您想要的。

本教程解释了这一点:

  

必须同步这些字段的所有更新,但没有理由阻止c1的更新与c2的更新交错 - 这样做会通过创建不必要的阻塞来减少并发性。我们不是使用同步方法或使用与此相关联的锁,而是仅创建两个对象来提供锁。

因此,他们还尝试允许c1的更新和c2的更新同时发生("交错")并且不会相互阻止,同时确保更新受到保护。

  

假设我在几个线程访问的同一个类中有两个公共同步方法。是不是两个方法永远不会同时执行?

如果一个线程在对象的synchronized方法中工作,则如果另一个线程尝试同一个对象的相同或另一个synchronized方法,则会阻止另一个线程。线程可以同时在不同的对象上运行方法。

  

如果答案是肯定的,是否有一种内置机制可以阻止一种方法饥饿(与其他方法相比,从未执行过或执行次数太少)?

如前所述,这是由操作系统的本机线程构造处理的。所有现代操作系统'处理线程饥饿,如果线程具有不同的优先级,这一点尤其重要。