简体   繁体   English

Java同步锁

[英]Java Synchronization Lock

When we say we lock on an object using the synchronized keyword, does it mean we are acquiring a lock on the whole object or only at the code that exists in the block? 当我们说使用synchronized关键字锁定一个对象时,它是否意味着我们正在获取整个对象的锁定或仅获取块中存在的代码?

In the following example listOne.add is synchronized, does it mean if another thread accesses listOne.get it would be blocked until the first thread gets out of this block? 在以下示例中, listOne.add是同步的,是否意味着如果另一个线程访问listOne.get ,它将被阻塞,直到第一个线程离开此块为止? What if a second thread accesses the listTwo.get or listTwo.add methods on the instance variables of the same object when the first thread is still in the synchronized block? 如果第二个线程在第一个线程仍在同步块中时访问同一对象的实例变量上的listTwo.getlistTwo.add方法,该怎么办?

List<String> listONe = new ArrayList<String>();
List<String> listTwo = new ArrayList<String>();

/* ... ... ... */

synchronized(this) {
    listOne.add(something);
}

The lock is on the object instance that you include in the synchronized block. 锁定在您包含在synchronized块中的对象实例上。

But take care! 但要小心! That object is NOT intrinsically locked for access by other threads. 该对象本质上不会被其他线程锁定。 Only threads that execute the same synchronized(obj) , where obj is this in your example but could in other threads also be a variable reference, wait on that lock. 只有执行相同的线程synchronized(obj) ,其中obj在你的例子中是this但在其他线程中也可以是变量引用,等待该锁定。

Thus, threads that don't execute any synchronized statements can access any and all variables of the 'locked' object and you'll probably run into race conditions. 因此,不执行任何同步语句的线程可以访问“锁定”对象的任何和所有变量,并且您可能会遇到竞争条件。

Given the methods: 鉴于方法:

  public void a(String s) {
    synchronized(this) {
      listOne.add(s);
    }
  }

  public void b(String s) {
    synchronized(this) {
      listTwo.add(s);
    }
  }

  public void c(String s) {
      listOne.add(s);
  }

  public void d(String s) {
      synchronized(listOne) {
        listOne.add(s);
      }
  }

You can not call a and b at the same time, as they are locked on the same lock. 你不能同时调用a和b,因为它们被锁定在同一个锁上。 You can however call a and c at the same time (with multiple threads obviously) as they are not locked on the same lock. 但是,您可以同时调用a和c(显然有多个线程),因为它们没有锁定在同一个锁上。 This can lead to trouble with listOne. 这可能会导致listOne出现问题。

You can also call a and d at the same time, as d is no different in this context from c. 您也可以同时调用a和d,因为d在此上下文中与c没有区别。 It does not use the same lock. 它不使用相同的锁。

It is important that you always lock listOne with the same lock, and allow no access to it without a lock. 始终使用相同的锁锁定listOne非常重要,并且在没有锁定的情况下不允许访问它。 If listOne and listTwo are somehow related and sometimes need updates at the same time / atomically you'd need one lock for access to both of them. 如果listOne和listTwo以某种方式相关并且有时需要同时/原子地更新,则需要一个锁来访问它们。 Otherwise 2 separate locks may be better. 否则2个单独的锁可能会更好。

Of course, you'd probably use the relatively new java.util.concurrent classes if all you need is a concurrent list :) 当然,如果您需要的是并发列表,您可能会使用相对较新的java.util.concurrent类:)

Other threads will block only on if you have a synchronized block on the same instance. 如果在同一实例上有同步块,则其他线程将仅阻止。 So no operations on the lists themselves will block. 因此,列表本身的任何操作都不会阻止。

synchronized(this) {

will only lock the object this . 只会锁定对象this To lock and work with the object listOne : 锁定和使用对象listOne

synchronized(listOne){
    listOne.add(something);
}

so that listOne is accessed one at a time by multiple threads. 以便多个线程一次访问一个listOne。

See: http://download.oracle.com/javase/tutorial/essential/concurrency/locksync.html 请参阅: http//download.oracle.com/javase/tutorial/essential/concurrency/locksync.html

You need to understand that the lock is advisory and is not physically enforced. 您需要了解锁定是建议性的,并且没有实际执行。 For example if you decided that you where going to use an Object to lock access to certain class fields, you must write the code in such a way to actually acquire the lock before accessing those fields. 例如,如果您决定使用Object来锁定对某些类字段的访问权限,则必须以这样的方式编写代码,以便在访问这些字段之前实际获取锁定。 If you don't you can still access them and potentially cause deadlocks or other threading issues. 如果不这样做,您仍然可以访问它们并可能导致死锁或其他线程问题。

The exception to this is the use of the synchronized keyword on methods where the runtime will automatically acquire the lock for you without you needing to do anything special. 例外情况是在方法上使用synchronized关键字,运行时将自动为您获取锁定而无需执行任何特殊操作。

The Java Language specification defines the meaning of the synchronized statement as follows: Java语言规范定义synchronized语句的含义,如下所示:

A synchronized statement acquires a mutual-exclusion lock (§17.1) on behalf of the executing thread, executes a block, then releases the lock. synchronized语句代表执行线程获取互斥锁(第17.1节),执行块,然后释放锁。 While the executing thread owns the lock, no other thread may acquire the lock. 当执行线程拥有锁时,没有其他线程可以获取锁。

 SynchronizedStatement:` synchronized ( Expression ) Block` 

The type of Expression must be a reference type, or a compile-time error occurs. Expression的类型必须是引用类型,否则会发生编译时错误。

A synchronized statement is executed by first evaluating the Expression. 通过首先评估表达式来执行同步语句。

If evaluation of the Expression completes abruptly for some reason, then the synchronized statement completes abruptly for the same reason. 如果表达式的评估由于某种原因突然完成,则同步语句由于同样的原因而突然完成。

Otherwise, if the value of the Expression is null, a NullPointerException is thrown. 否则,如果Expression的值为null,则抛出NullPointerException。

Otherwise, let the non-null value of the Expression be V. The executing thread locks the lock associated with V. Then the Block is executed. 否则,让表达式的非空值为V.执行线程锁定与V关联的锁。然后执行块。 If execution of the Block completes normally, then the lock is unlocked and the synchronized statement completes normally. 如果块的执行正常完成,则解锁并且synchronized语句正常完成。 If execution of the Block completes abruptly for any reason, then the lock is unlocked and the synchronized statement then completes abruptly for the same reason. 如果块的执行因任何原因突然完成,则锁定被解锁,然后同步语句突然完成,原因相同。

Acquiring the lock associated with an object does not of itself prevent other threads from accessing fields of the object or invoking unsynchronized methods on the object. 获取与对象关联的锁本身并不会阻止其他线程访问对象的字段或在对象上调用未同步的方法。 Other threads can also use synchronized methods or the synchronized statement in a conventional manner to achieve mutual exclusion. 其他线程也可以以传统方式使用同步方法或同步语句来实现互斥。

That is, in your example 也就是说,在你的例子中

synchronized(this) {
    listOne.add(something);
}

the synchronized block does treat the object referred to by listOne in any special way, other threads may work with it as they please. synchronized块确实以任何特殊方式处理listOne引用的对象,其他线程可以listOne使用它。 However, it ensures that no other thread may enter a synchronized block for the object referred to by this at the same time. 然而,它确保没有其他线程可以进入同步块用于通过引用的对象this同时。 Therefore, if all code working with listOne is in synchronized blocks for the same object, at most one thread may work with listOne at any given time. 因此,如果使用listOne所有代码都在同一对象的同步块中,则在任何给定时间最多一个线程可以与listOne一起使用。

Also note that the object being locked on gets no special protection from concurrent access of its state, so the code 另请注意,被锁定的对象没有特别保护其状态的并发访问,因此代码

void increment() {
    synchronized (this) {
        this.counter = this.counter + 1;
    }
}

void reset() {
    this.counter = 0;
}

is incorrectly synchronized, as a second thread may execute reset while the first thread has read, but not yet written, counter , causing the reset to be overwritten. 错误地同步,因为第二个线程可能在第一个线程已读取但尚未写入counter执行reset ,从而导致重置被覆盖。

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

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