`
youyu4
  • 浏览: 440075 次
社区版块
存档分类
最新评论

Java多线程之Lock详解

 
阅读更多

Java多线程之Lock详解

 

Lock的工作原理

 

获得锁

  • 从线程中读取表示锁状态的变量
  • 如果状态为0,就改为1,如果有多个线程,只会有一个成功
  • 如果修改成功就获得了锁,进入维护队列
  • 如果失败,则进入等待队列并阻塞自身,此时线程一直被阻塞在lock方法中,没有从该方法中返回
  • 如果表示状态的变量的值为1,那么将当前线程放入等待队列中,然后将自身阻塞(被唤醒后仍然在lock方法中,并从下一条语句继续执行,这里又会回到第1步重新开始)

注意: 唤醒并不表示线程能立刻运行,而是表示线程处于就绪状态,仅仅是可以运行而已

 

释放锁

  • 释放锁的线程将状态变量的值从1设置为0,并唤醒等待(锁)队列中的队首节点,释放锁的线程从就从unlock方法中返回,继续执行线程后面的代码
  • 被唤醒的线程(队列中的队首节点)和可能和未进入队列并且准备获取的线程竞争获取锁,重复获取锁的过程

注意:可能有多个线程同时竞争去获取锁,但是一次只能有一个线程去释放锁,队列中的节点都需要它的前一个节点将其唤醒,例如有队列A<-B-<C ,即由A释放锁时唤醒B,B释放锁时唤醒C

 

 

 

主要方法和使用

 

实际上Lock是一个接口,常用的方法有:

 

//尝试获取锁,获取成功则返回,否则阻塞当前线程
void lock(); 

//尝试获取锁,线程在成功获取锁之前被中断,则放弃获取锁,抛出异常 
void lockInterruptibly() throws InterruptedException; 

//尝试获取锁,获取锁成功则返回true,否则返回false 
boolean tryLock(); 

//尝试获取锁,若在规定时间内获取到锁,则返回true,否则返回false,未获取锁之前被中断,则抛出异常 
boolean tryLock(long time, TimeUnit unit) 
                                   throws InterruptedException; 

//释放锁
void unlock(); 

//返回当前锁的条件变量,通过条件变量可以实现类似notify和wait的功能,一个锁可以有多个条件变量
Condition newCondition();

 

 

使用方法

 

多线程下访问(互斥)共享资源时, 访问前加锁,访问结束以后解锁,解锁的操作推荐放入finally块中。

 

Lock l = ...; //根据不同的实现Lock接口类的构造函数得到一个锁对象 
l.lock(); //获取锁位于try块的外面 
try { 
      // access the resource protected by this lock 
} finally { 
     l.unlock(); 
}

 

注意,加锁位于对资源访问的try块的外部,特别是使用lockInterruptibly方法加锁时就必须要这样做,这为了防止线程在获取锁时被中断,这时就不必(也不能)释放锁。

 

try {
     l.lockInterruptibly();//获取锁失败时不会执行finally块中的unlock语句
      try{
          // access the resource protected by this lock
     }finally{
          l.unlock();
     }
} catch (InterruptedException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
}

 

 

 

 

 

Lock的实现

 

Lock有三个实现类,一个是ReentrantLock,另两个是ReentrantReadWriteLock类中的两个静态内部类ReadLockWriteLock

 

  • ReentrantLock(可重入锁):互斥锁,次只能一个线程拥有互斥锁,其他线程只有等待
  • ReentrantReadWriteLock(读写锁):将读和写分离开两个锁,读锁不互斥,写锁互斥
  • 自旋锁:一次只能有一个进程进入临界区,读写锁是自旋锁的一个特例。

 

 

ReentrantLock特性

 

  • 轮询锁:在每次请求资源的时候,就用tryLock(),如果资源被占用,就返回,而不是一直阻塞。
  • 定时锁:在进行轮询的时候,还可以设置时间,超时后就返回,而不是一直阻塞。
  • 公平性:所谓公平锁,线程将按照他们发出请求的顺序来获取锁,不允许插队;但在非公平锁上,则允许插队。
  • 可中断锁:lockInterruptibly方法能够在获取锁的同时保持对中断的响应,因此无需创建其它类型的不可中断阻塞操作。

ReentrantLock最大的作用

 

  • 避免死锁
  • 使用灵活,自己加锁和解锁

 

 

读写锁特性

 

  • 拥有可重入锁特性的情况下,还有下面特性
  • 多个读者可以同时进行读
  • 写者必须互斥(只允许一个写者写,也不能读者写者同时进行)
  • 写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)

读写锁最大的作用

 

  • 提高读写速度,当很多读线程,很少写线程时,可以选择使用

 

 

应用场景

 

1. 以队列操作为例:

 

线程A对队列负责将数据写入队列。须采取“互斥锁”或“读写锁的写锁”

 

线程B队列负责从队列读出数据。须采取“互斥锁”或“读写锁的写锁”,读队列操作,不可采取“读写锁的读锁”,因为从队列读出数据时,需要更改队列本身的下标索引,如果多个线程同时操作该队列的话,就会导致队列下标索引混乱。

 

但是对队列的查询操作则最好采取“读写锁的读锁”来提高效率。

 

 

 

 

Condition接口

 

一个Condition实例的内部实际上维护了一个队列,队列中的节点表示由于(某些条件不满足而)线程自身调用await方法阻塞的线程。Condition接口中有两个重要的方法,即 await方法和 signal方法。

 

线程调用这个方法之前该线程必须已经获取了Condition实例所依附的锁。

这样的原因有两个

 

  • 对于await方法,它内部会执行释放锁的操作,所以使用前必须获取锁。
  • 对于signal方法,是为了避免多个线程同时调用同一个Condition实例的singal方法时引起的(队列)出列竞争。

Condition方法的执行流程

 

await方法:

 

      1. 入列到条件队列(注意这里不是等待锁的队列)

 

      2. 释放锁

 

      3. 阻塞自身线程

 

      ------------被唤醒后执行-------------

 

      4. 尝试去获取锁(执行到这里时线程已不在条件队列中,而是位于等待(锁的)队列中,参见signal方法)

 

            4.1 成功,从await方法中返回,执行线程后面的代码

 

            4.2 失败,阻塞自己(等待前一个节点释放锁时将它唤醒)

 

 

注意:await方法时自身线程调用的,线程在await方法中阻塞,并没有从await方法中返回,当唤醒后继续执行await方法中后面的代码(也就是获取锁的代码)。可以看出await方法释放了锁,又尝试获得锁。当获取锁不成功的时候当前线程仍然会阻塞到await方法中,等待前一个节点释放锁后再将其唤醒。

 

 

signal方法:

 

      1. 将条件队列的队首节点取出,放入等待锁队列的队尾

 

      2. 唤醒该节点对应的线程

 

注意:signal是由其它线程调用

 



 

Condition主要方法

// 让线程进入等通知待状态 
void await() throws InterruptedException; 
void awaitUninterruptibly();
 
//让线程进入等待通知状态,超时结束等待状态,并抛出异常  
long awaitNanos(long nanosTimeout) throws InterruptedException; 
boolean await(long time, TimeUnit unit) throws InterruptedException; 
boolean awaitUntil(Date deadline) throws InterruptedException; 

//将条件队列中的一个线程,从等待通知状态转换为等待锁状态 
void signal(); 

//将条件队列中的所有线程,从等待通知阻塞状态转换为等待锁阻塞状态
void signalAll();

 

 

 

Lock和Condition一起使用

 

下面这个例子,就是利用lock和condition实现B线程先打印一句信息后,然后A线程打印两句信息(不能中断),交替十次后结束

public class ConditionDemo {
    volatile int key = 0;
    Lock l = new ReentrantLock();
    Condition c = l.newCondition();
    
    public static  void main(String[] args){
        ConditionDemo demo = new ConditionDemo();
        new Thread(demo.new A()).start();
        new Thread(demo.new B()).start();
    }
    
    class A implements Runnable{
        @Override
        public void run() {
            int i = 10;
            while(i > 0){
                l.lock();
                try{
                    if(key == 1){
                        System.out.println("A is Running");
                        System.out.println("A is Running");
                        i--;
                        key = 0;
                        c.signal();
                    }else{
                     c.awaitUninterruptibly();                        
                    }
                    
                }
                finally{
                    l.unlock();
                }
            }
        }
        
    }
    
    class B implements Runnable{
        @Override
        public void run() {
            int i = 10;
            while(i > 0){
                l.lock();
                try{
                    if(key == 0){
                        System.out.println("B is Running");
                        i--;
                        key = 1;
                        c.signal();
                    }else{
                     c.awaitUninterruptibly();                        
                    }
                    
                }
                finally{
                    l.unlock();
                }
            }
        }    
    }
}

 

 

  • 大小: 59 KB
分享到:
评论

相关推荐

    java多线程设计模式详解(PDF及源码)

    (注意,本资源附带书中源代码可供参考) 多线程与并发处理是程序设计好坏优劣的重要课题,本书通过浅显易懂的文字与实例来介绍Java线程相关的设计模式概念,并且通过实际的Java程序范例和 UML图示来一一解说,书中...

    Java多线程详解及示例

    Java多线程编程是提升程序性能和响应性的关键技术。理解多线程的概念,掌握线程的创建、同步、通信、死锁避免等核心知识点,以及合理使用线程池,对于编写高效、稳定的并发程序至关重要。通过实践,开发者可以更好地...

    Java多线程编程深入详解.docx

    Java多线程编程深入详解 多线程编程是Java编程语言中的一种重要技术,用于提高程序的执行效率和响应速度。在本文中,我们将深入探讨Java多线程编程的基础知识和高级技术。 什么是多进程和多线程? 在计算机科学中...

    java多线程设计模式详解(PDF及源码).zip

    Java多线程设计模式是...通过阅读“java多线程设计模式详解(PDF及源码)”的资料,我们可以深入了解这些知识点,并通过提供的源码加深理解,学习如何在实际项目中应用多线程设计模式,提高程序的并发性能和可维护性。

    java多线程设计模式详解+源码

    本资料包包含“java多线程设计模式详解”文档以及对应的源码,将帮助你深入理解并熟练运用Java多线程设计模式。 1. **线程的基本概念**:在Java中,线程是程序执行的最小单元,每个线程都有自己的程序计数器、...

    JAVA多线程设计模式详解

    总之,“JAVA多线程设计模式详解”全面介绍了Java多线程技术,从基础知识到高级设计模式,为读者提供了扎实的理论基础和实践经验,有助于提升Java多线程编程的能力。通过学习和实践书中的内容,开发者能够更好地应对...

    java多线程详解

    ### Java多线程详解:深度探索Java线程机制 #### 知识点一:线程与进程的区别 在深入探讨Java多线程之前,我们首先需要理解线程与进程的基本概念及其区别。进程是资源分配的基本单位,拥有独立的内存空间,而线程...

    java多线程设计模式详解.rar

    本压缩包文件“java多线程设计模式详解.rar”显然提供了深入探讨这一主题的详细资料。 一、线程基础 在Java中,线程是程序中的执行流,每个线程都有自己的程序计数器、虚拟机栈、本地方法栈和堆。主线程启动后,...

    java多线程详解(比较详细的阐述了多线程机制)

    Java多线程是Java编程中的重要概念,它允许程序同时执行多个任务,从而提升系统效率和资源利用率。本文将深入探讨Java多线程机制,包括线程的创建、同步、通信以及常见设计模式。 首先,Java中创建线程主要有两种...

    java 多线程编程实战指南(核心 + 设计模式 完整版)

    《Java多线程编程实战指南》这本书深入浅出地讲解了Java多线程的核心概念和实战技巧,分为核心篇和设计模式篇,旨在帮助开发者掌握并应用多线程技术。 1. **线程基础** - **线程的创建**:Java提供了两种创建线程...

    java多线程设计模式详解

    这份PDF文档,"java多线程设计模式详解",提供了一种深入理解如何在Java环境中高效利用多线程并保证程序稳定性的途径。下面,我们将详细探讨多线程设计模式的相关知识点。 1. **生产者消费者模式**:这种模式通过...

    JAVA多线程练习题答案。

    JAVA多线程练习题答案详解 在本文中,我们将对 JAVA 多线程练习题的答案进行详细的解释和分析。这些题目涵盖了 JAVA 多线程编程的基本概念和技术,包括线程的生命周期、线程同步、线程状态、线程优先级、线程安全等...

    java多线程机制 详解

    Java的多线程机制是Java语言的一大特性,它允许程序同时执行多个任务,提升程序响应速度,优化资源利用率。在Java中,线程是程序执行的最小单位,一个进程可以包含多个线程,每个线程都有自己独立的生命周期,包括...

    java+多线程+同步详解源码整理

    Java多线程允许程序同时执行多个独立的线程,从而提高计算机系统的资源利用率和程序的响应速度。Java提供了两种创建线程的方式:通过实现`Runnable`接口或继承`Thread`类。实现`Runnable`接口更为灵活,因为Java不...

    JAVA单线程多线程

    ### JAVA中的单线程与多线程概念解析 #### 单线程的理解 在Java编程环境中,单线程指的是程序执行过程中只有一个线程在运行。这意味着任何时刻只能执行一个任务,上一个任务完成后才会进行下一个任务。单线程模型...

    java多线程设计模式详解PDF及源码

    以上内容是Java多线程设计模式的核心知识点,通过阅读提供的"java多线程设计模式详解(PDF及源码)",你可以深入理解这些概念,并通过实际代码加深印象。学习这些模式和技巧将有助于编写出更加高效、稳定的多线程Java...

    java+多线程+同步详解源代码学习

    Java多线程与同步是Java编程中的核心概念,它们在构建高效、响应迅速的应用程序时起着至关重要的作用。在大型系统开发中,多线程技术使得程序能够同时执行多个任务,提高系统的并发性,而同步机制则确保了在多线程...

Global site tag (gtag.js) - Google Analytics