`
niwtsew
  • 浏览: 71934 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Locks in Java

阅读更多

copied from http://tutorials.jenkov.com/java-concurrency/locks.html 

Notes:This tutorial is a very easy to read and understand version of java concurrent lock mechanism. I do suggest to take a look.

 

Locks in Java 

By Jakob Jenkov

 

A lock is a thread synchronization mechanism like synchronized blocks except locks can be more sophisticated than Java's synchronized blocks. Locks (and other more advanced synchronization mechanisms) are created using synchronized blocks, so it is not like we can get totally rid of the synchronized keyword.

From Java 5 the package java.util.concurrent contains several lock implementations, so you may not have to implement your own locks. But you will still need to know how to use them, and it can still be useful to know the theory behind their implementation.

Here is a list of the topics covered in this text:

  1. A Simple Lock
  2. Lock Reentrance
  3. Lock Fairness
  4. Calling unlock() From a finally-clause

A Simple Lock

Let's start out by looking at a synchronized block of Java code:

public class Counter{

  private int count = 0;

  public int inc(){
    synchronized(this){
      return ++count;
    }
  }
}

Notice the synchronized(this) block in the inc() method. This block makes sure that only one thread can execute the return ++count at a time. The code in the synchronized block could have been more advanced, but the simple ++count suffices to get the point across.

The Counter class could have been written like this instead, using a Lock instead of a synchronized block:

public class Counter{

  private Lock lock = new Lock();
  private int count = 0;

  public int inc(){
    lock.lock();
    int newCount = ++count;
    lock.unlock();
    return newCount;
  }
}

The lock() method locks the Lock instance so that all threads calling lock() are blocked until unlock() is executed.

Here is a simple Lock implementation:

public class Lock{
  
  private boolean isLocked = false;
  
  public synchronized void lock()
  throws InterruptedException{
    while(isLocked){
      wait();
    }
    isLocked = true;
  }
  
  public synchronized void unlock(){
    isLocked = false;
    notify();
  }
}

Notice the while(isLocked) loop, which is also called a "spin lock". Spin locks and the methods wait() and notify() are covered in more detail in the text Thread Signaling . While isLocked is true, the thread calling lock() is parked waiting in the wait() call. In case the thread should return unexpectedly from the wait() call without having received a notify() call (AKA a Spurious Wakeup ) the thread re-checks the isLocked condition to see if it is safe to proceed or not, rather than just assume that being awakened means it is safe to proceed. If isLocked is false, the thread exits the while(isLocked) loop, and sets isLocked back to true, to lock the Lock instance for other threads calling lock() .

When the thread is done with the code in the critical section (the code between lock() and unlock() ), the thread calls unlock() . Executing unlock() sets isLocked back to false, and notifies (awakens) one of the threads waiting in the wait() call in the lock() method, if any.

Lock Reentrance

Synchronized blocks in Java are reentrant. This means, that if a Java thread enters a synchronized block of code, and thereby take the lock on the monitor object the block is synchronized on, the thread can enter other Java code blocks synchronized on the same monitor object. Here is an example:

public class Reentrant{

  public synchronized outer(){
    inner();
  }

  public synchronized inner(){
    //do something
  }
}

Notice how both outer() and inner() are declared synchronized, which in Java is equivalent to a synchronized(this) block. If a thread calls outer() there is no problem calling inner() from inside outer() , since both methods (or blocks) are synchronized on the same monitor object ("this"). If a thread already holds the lock on a monitor object, it has access to all blocks synchronized on the same monitor object. This is called reentrance. The thread can reenter any block of code for which it already holds the lock.

The lock implementation shown earlier is not reentrant. If we rewrite the Reentrant class like below, the thread calling outer() will be blocked inside the lock.lock() in the inner() method.

public class Reentrant2{

  Lock lock = new Lock();

  public outer(){
    lock.lock();
    inner();
    lock.unlock();
  }

  public synchronized inner(){
    lock.lock();
    //do something
    lock.unlock();
  }
}

A thread calling outer() will first lock the Lock instance. Then it will call inner() . Inside the inner() method the thread will again try to lock the Lock instance. This will fail (meaning the thread will be blocked), since the Lock instance was locked already in the outer() method.

The reason the thread will be blocked the second time it calls lock() without having called unlock() in between, is apparent when we look at the lock() implementation:

public class Lock{

  boolean isLocked = false;

  public synchronized void lock()
  throws InterruptedException{
    while(isLocked){
      wait();
    }
    isLocked = true;
  }

  ...
}

It is the condition inside the while loop (spin lock) that determines if a thread is allowed to exit the lock() method or not. Currently the condition is that isLocked must be false for this to be allowed, regardless of what thread locked it.

To make the Lock class reentrant we need to make a small change:

public class Lock{

  boolean isLocked = false;
  Thread  lockedBy = null;

  public synchronized void lock()
  throws InterruptedException{
    Thread callingThread = Thread.currentThread();
    while(isLocked && lockedBy != callingThread){
      wait();
    }
    isLocked = true;
    lockedBy = callingThread;
  }

  ...
}

Notice how the while loop (spin lock) now also takes the thread that locked the Lock instance into consideration. If either the lock is unlocked (isLocked = false) or the calling thread is the thread that locked the Lock instance, the while loop will not execute, and the thread calling lock() will be allowed to exit the method. The Lock class is now reentrant.

Lock Fairness

Java's synchronized blocks makes no guarantees about the sequence in which threads trying to enter them are granted access. Therefore, if many threads are constantly competing for access to the same synchronized block, there is a risk that one or more of the threads are never granted access - that access is always granted to other threads. This is called starvation. To avoid this a Lock should be fair. Since the Lock implementations shown in this text uses synchronized blocks internally, they do not guarantee fairness. Starvation and fairness are discussed in more detail in the text Starvation and Fairness .

Calling unlock() From a finally-clause

When guarding a critical section with a Lock , and the critical section may throw exceptions, it is important to call the unlock() method from inside a finally -clause. Doing so makes sure that the Lock is unlocked so other threads can lock it. Here is an example:

lock.lock();
try{
  //do critical section code, which may throw exception
} finally {
  lock.unlock();
}

This little construct makes sure that the Lock is unlocked in case an exception is thrown from the code in the critical section. If unlock() was not called from inside a finally -clause, and an exception was thrown from the critical section, the Lock would remain locked forever, causing all threads calling lock() on that Lock instance to halt indefinately. The only thing that could unlock the Lock again would be if the Lock is reentrant, and the thread that had it locked when the exception was thrown, later succeeds in locking it, executing the critical section and calling unlock() again afterwards. That would unlock the Lock again. But why wait for that to happen, if it happens? Calling unlock() from a finally -clause is a much more robust solution.

分享到:
评论

相关推荐

    Thinking in Java 习题答案

    6. **多线程**:Java提供了丰富的API来支持并发编程,如Thread类、synchronized关键字、volatile变量、Lock接口等。习题会涵盖同步控制、线程通信等复杂问题。 7. **输入/输出(I/O)**:Java的I/O系统包括文件操作、...

    thinking in java 文档

    《Thinking in Java》是Bruce Eckel的经典著作,它深入浅出地介绍了Java编程语言的核心概念和技术,对于初学者和有经验的程序员都是极好的学习资源。文档内容涵盖了许多关键知识点,包括但不限于: 1. **基础语法**...

    thinking in java4 源码

    《Thinking in Java》是Bruce Eckel的经典之作,它深入浅出地介绍了Java语言的核心概念和技术。这本书的第四版更是被广大Java开发者视为学习和进阶的重要参考资料。源码是书中理论知识的实践体现,通过阅读和理解...

    The Thinking in Java Annotated Solution Guide

    《The Thinking in Java》是 Bruce Eckel 所著的一本经典Java编程教材,以其深入浅出的讲解方式深受程序员喜爱。这本书旨在引导读者深入理解Java语言的核心概念和机制,不仅覆盖了基础语法,还包括高级特性如多线程...

    Think In Java 4(完美高清中文版).

    5. **多线程篇**:介绍Java并发编程的基础,包括线程的创建、同步机制(如synchronized关键字和Lock接口)以及并发工具类,如Semaphore、ExecutorService等。 6. **输入/输出篇**:探讨Java的I/O流系统,包括文件I/...

    Thinking in Java知识点总结

    《Thinking in Java》是 Bruce Eckel 的经典著作,其中第21章主要讲解了Java中的并发编程知识。并发编程是让程序在多个线程中同时执行任务,以提高程序的效率和响应性。以下是该章节的重点知识点: 1. **并发的多面...

    Data Structures and Algorithms in Java

    《Data Structures and Algorithms in Java》是一本专注于Java编程语言中的数据结构与算法的权威书籍,由Michael T. Goodrich和Roberto Tamassia合著,第五版提供了全面且深入的讲解。这本书对于学习和理解如何在...

    Thinking in java edition3-code

    《Thinking in Java》是Bruce Eckel的经典编程教材,第三版(edition3)深入浅出地介绍了Java编程语言的核心概念和技术。这个压缩包文件包含了书中所提及的示例代码,名为"TIJcode",这对于理解和实践书中理论知识至...

    Java并发编程实践(java concurrency in practice)pdf (java多线程总结.ppt)

    书中会讲解监视器(Monitor)、锁(Lock)、条件变量(Condition)以及它们在`synchronized`关键字中的应用。 3. **线程安全**:线程安全的类或方法能够正确处理多线程环境下的并发访问。书中讨论了线程安全的层次...

    Thinking in java 1

    很抱歉,但根据您给出的信息,标题"Thinking in java 1"和描述"Thinking in java JAVA学习的最好教材,没有之一"明显与压缩包子文件的文件名称"叛逆的鲁路修Stories.mp4"不符。"Thinking in Java"通常指的是Bruce ...

    Java并发编程:设计原则与模式(Concurrent.Programming.in.Java)(中英版)

    这通常通过同步机制实现,如`synchronized`关键字、`volatile`变量、`Lock`接口等。 2. **最小化锁的使用**:尽可能减少对共享资源的锁定,以提高并行性。例如,使用无锁数据结构或原子操作(`java.util.concurrent...

    Thinking in Java(英)

    7. **多线程**:Java提供了内置的多线程支持,包括Thread类和Runnable接口,以及同步机制如synchronized关键字和Lock接口。 8. **网络编程**:Java的Socket编程使得创建网络应用变得简单,书中的这部分可能会讲解...

    Thinking in Java英文版.rar

    《Thinking in Java》是Bruce Eckel创作的一本Java编程经典教程,深受全球程序员喜爱,尤其适合初学者和有经验的开发者深入理解Java语言。这本书以其深入浅出的讲解方式,全面覆盖了Java语言的核心概念、语法和编程...

    Thinking in java第三版源码

    6. **多线程**:Java提供了并发编程的支持,包括Thread类和Runnable接口,以及同步机制(如synchronized关键字、wait/notify机制和Lock接口)。源码将展示如何创建和管理线程。 7. **网络编程**:Java的Socket和...

    thinking in java 3rd中文

    5. **多线程**:Java提供了强大的多线程支持,包括线程的创建、同步机制(如synchronized关键字、wait/notify、Lock接口等)。理解并发编程对于开发高效系统至关重要。 6. **输入/输出**:包括I/O流的分类、文件...

    Thinking in Java 3

    《Thinking in Java 3》是Java编程领域里一本经典的教程,由Bruce Eckel撰写,它深入浅出地讲解了Java语言的核心概念和技术。这本书对于初学者和有经验的程序员来说都是一个宝贵的资源,因为它不仅涵盖了基础语法,...

    thinking in java 中文版第三版

    6. **多线程**:Java支持多线程编程,书中介绍了Thread类、Runnable接口以及同步机制,如synchronized关键字和Lock接口,帮助理解并发执行的概念。 7. **输入/输出流**:书中阐述了Java的I/O系统,包括文件操作、...

    Thinking in Java 3Edition(中英文)

    《Thinking in Java 3rd Edition》是一本深受程序员喜爱的经典Java编程教材,由Bruce Eckel撰写,旨在深入讲解Java语言的核心概念和编程思想。这本书以其详尽的解释、丰富的示例和深入的讨论,帮助读者建立起坚实的...

    thinking in java4 习题解答

    《Thinking in Java》是Bruce Eckel的经典编程教材,它深入浅出地介绍了Java语言的核心概念和技术。这本书的第四版更是涵盖了Java的最新发展,包括泛型、枚举、注解等。现在,我们来详细探讨一下这个压缩包文件中...

    thinking in java第三版

    《Thinking in Java》是Bruce Eckel创作的一本经典Java编程教程,被誉为学习Java的宝典。这本书的第三版深入浅出地介绍了Java语言的核心概念和技术,对于初学者和经验丰富的开发者来说,都是极具价值的学习资源。 ...

Global site tag (gtag.js) - Google Analytics