简体   繁体   English

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

[英]Questions about how the synchronized keyword works with locks and thread starvation

In this java tutorial there's some code that shows an example to explain the use of the synchronized keyword. 此Java教程中,有一些代码显示了一个示例,说明如何使用synchronized关键字。 My point is, why I shouldn't write something like this: 我的意思是,为什么我不应该这样写:

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++;
         }
     }
}

Without bothering create lock objects? 不用费心创建锁对象吗? Also, why bother instantiate that lock objects? 另外,为什么还要实例化该锁定对象? Can't I just pass a null reference? 我不能只传递一个空引用吗? I think I'm missing out something here. 我想我在这里错过了一些东西。

Also, assume that I've two public synchronized methods in the same class accessed by several thread. 另外,假设我在同一个类中有两个线程可以访问的两个公共同步方法。 Is it true that the two methods will never be executed at the same time? 两种方法永远不会同时执行是真的吗? If the answer is yes, is there a built-in mechanism that prevents one method from starvation (never been executed or been executed too few times compared to the other method)? 如果答案是肯定的,那么是否有一种内置机制可以防止一种方法出现饥饿现象(与另一种方法相比,从未执行过或执行过几次)?

As @11thdimension has replied, you cannot synchronize on a primitive type (eg., long). 如@ 11thdimension所答复,您无法在原始类型(例如long)上进行同步。 It must be a class object. 它必须是一个类对象。

So, you might be tempted to do something like the following: 因此,您可能会想执行以下操作:

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

This will not work properly, as "c1++" is a shortcut for "c1 = c1 + 1", which actually assigns a new object to c1, and as such, two threads might end up in the same block of synchronized code. 这将无法正常工作,因为“ c1 ++”是“ c1 = c1 + 1”的快捷方式,它实际上为c1分配了一个新对象,因此,两个线程可能最终出现在同一块同步代码中。

For the lock to work properly, the object being synchronized upon should not be reassigned. 为了使锁正常工作,不应重新分配正在同步的对象。 (Well, maybe in some rare circumstances where you really know what you are doing.) (好吧,也许在某些罕见的情况下,您真的知道自己在做什么。)

You cannot pass a null object to the synchronized(...) statement. 您不能将空对象传递给synced(...)语句。 Java is effectively creating semaphores on the ref'd object, and uses that information to prevent more than one thread accessing the same protected resource. Java有效地在引用对象上创建了信号灯,并使用该信息来防止多个线程访问相同的受保护资源。

You do not always need a separate lock object, as in the case of a synchronized method. 与同步方法一样,您并不总是需要单独的锁对象。 In this case, the class object instance itself is used to store the locking information, as if you used 'this' in the method iteslf: 在这种情况下,类对象实例本身用于存储锁定信息,就像在iteslf方法中使用了“ this”一样:

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

First you can not pass primitive variable to synchronized , it requires a reference. 首先,您不能将原始变量传递给synchronized ,它需要一个引用。 Second that tutorial is just a example showing guarded block. 其次,该教程只是显示受保护块的示例。 It's not c1,c2 that it's trying to protect but it's trying to protect all the code inside synchronized block. 它不是在保护c1,c2 ,而是在保护synchronized块中的所有代码。

JVM uses Operating system's scheduling algorithm. JVM使用操作系统的调度算法。

What is the JVM Scheduling algorithm? 什么是JVM调度算法?

So it's not JVM's responsibility to see if threads are starved. 因此,查看线程是否饥饿不是JVM的责任。 You can however assign priority of threads to prefer one over other to execute. 但是,您可以分配线程优先级以优先执行一个。

Every thread has a priority. 每个线程都有一个优先级。 Threads with higher priority are executed in preference to threads with lower priority. 具有较高优先级的线程优先于具有较低优先级的线程执行。 Each thread may or may not also be marked as a daemon. 每个线程可能会也可能不会被标记为守护程序。 When code running in some thread creates a new Thread object, the new thread has its priority initially set equal to the priority of the creating thread, and is a daemon thread if and only if the creating thread is a daemon. 当在某个线程中运行的代码创建新的Thread对象时,新线程的优先级最初设置为与创建线程的优先级相等,并且当且仅当创建线程是守护程序时,该线程才是守护程序线程。

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

If you're concerned about this scenario then you have to implement it yourself. 如果您担心这种情况,则必须自己实施。 Like maintaining a thread which checks for starving threads and as time passes it increases the priority of the threads which have been waiting longer than others. 就像维护检查饥饿线程的线程一样,随着时间的流逝,它增加了比其他线程等待时间更长的线程的优先级。

Yes it's true that two method which have been synchronized will never be executed on the same instance simultaneously. 是的,确实两个同步的方法永远不会在同一实例上同时执行。

Why bother instantiate that lock objects? 为什么要实例化该锁定对象? Can't I just pass a null reference? 我不能只传递一个空引用吗?

As others have mentioned, you cannot lock on long c1 because it is a primitive. 正如其他人提到的那样,您不能锁定long c1因为它是原始的。 Java locks on the monitor associated with an object instance. Java锁定与对象实例关联的监视器。 This is why you also can't lock on null . 这就是为什么您也不能锁定null

The thread tutorial is trying to demonstrate a good pattern which is to create private final lock objects to precisely control the mutex locations that you are trying to protect. 线程教程正在尝试演示一个好的模式,该模式是创建private final锁定对象以精确控制您要保护的互斥锁的位置。 Calling synchronized on this or other public objects can cause external callers to block your methods which may not be what you want. this public对象或其他public对象上进行synchronized调用可能会导致外部调用者阻止您可能不需要的方法。

The tutorial explains this: 本教程对此进行了解释:

All updates of these fields must be synchronized, but there's no reason to prevent an update of c1 from being interleaved with an update of c2 — and doing so reduces concurrency by creating unnecessary blocking. 这些字段的所有更新都必须同步,但是没有理由阻止c1更新与c2更新交织—这样做会通过创建不必要的阻塞来减少并发性。 Instead of using synchronized methods or otherwise using the lock associated with this, we create two objects solely to provide locks. 代替使用同步方法或以其他方式使用与此关联的锁,我们仅创建两个对象来提供锁。

So they are also trying to allow updates to c1 and updates to c2 to happen concurrently ("interleaved") and not block each other while at the same time making sure that the updates are protected. 因此,他们还试图允许对c1更新和对c2更新同时发生(“交织”),并且在确保更新受到保护的同时不会相互阻塞。

Assume that I've two public synchronized methods in the same class accessed by several thread. 假设我在同一类中有两个线程可以访问的两个公共同步方法。 Is it true that the two methods will never be executed at the same time? 两种方法永远不会同时执行是真的吗?

If one thread is working in a synchronized method of an object, another thread will be blocked if it tries the same or another synchronized method of the same object . 如果一个线程正在使用对象的synchronized方法工作,则另一个线程如果尝试使用同一对象相同方法或另一个synchronized方法,则会被阻塞。 Threads can run methods on different objects concurrently. 线程可以在不同对象上同时运行方法。

If the answer is yes, is there a built-in mechanism that prevents one method from starvation (never been executed or been executed too few times compared to the other method)? 如果答案是肯定的,那么是否有一种内置机制可以防止一种方法出现饥饿现象(与另一种方法相比,从未执行过或执行过几次)?

As mentioned, this is handled by the native thread constructs from the operating system. 如前所述,这是由操作系统的本地线程结构处理的。 All modern OS' handle thread starvation which is especially important if the threads have different priorities. 所有现代OS都处理线程匮乏,如果线程具有不同的优先级,这一点尤其重要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM