类Condition和类ReentrantLock的使用

先来看一段代码,实现如下打印效果:

1 2 A 3 4 B 5 6 C 7 8 D 9 10 E 11 12 F 13 14 G 15 16 H 17 18 I 19 20 J 21 22 K 23 24 L 25 26 M 27 28 N 29 30 O 31 32 P 33 34 Q 35 36 R 37 38 S 39 40 T 41 42 U 43 44 V 45 46 W 47 48 X 49 50 Y 51 52 Z 

package test;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyThread {

    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition condition1 = lock.newCondition();
        Condition condition2 = lock.newCondition();
        ThreadA a = new ThreadA(lock, condition1, condition2);
        ThreadB b = new ThreadB(lock, condition1, condition2);
        a.start();
        b.start();
    }
}

class ThreadA extends Thread {

    Lock      lock;
    Condition condition1;
    Condition condition2;

    public ThreadA(Lock lock, Condition condition1, Condition condition2){
        super();
        this.lock = lock;
        this.condition1 = condition1;
        this.condition2 = condition2;
    }

    @Override
    public void run() {
        try {
            lock.lock();
            for (int i = 1; i <= 52; i++) {
                if (i % 2 != 0) {
                    System.out.print(i + " " + (i + 1));
                    condition2.signal();
                } else {
                    condition1.await();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

class ThreadB extends Thread {

    Lock      lock;
    Condition condition1;
    Condition condition2;

    public ThreadB(Lock lock, Condition condition1, Condition condition2){
        super();
        this.lock = lock;
        this.condition1 = condition1;
        this.condition2 = condition2;
    }

    @Override
    public void run() {
        try {
            lock.lock();
            for (int i = (int) ‘A‘; i < (int) (‘A‘ + 52); i++) {
                System.out.print(" " + (char) i + " ");
                condition1.signal();
                condition2.await();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

上面的代码是我们在项目中使用类Condition和类ReentrantLock的示例,接下来对这几个概念阐述。

Synchronized和Lock的简单对比:

synchronized是java中的一个关键字,也就是说是Java语言内置的特性。那么为什么会出现Lock呢?

  我们知道如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:

  1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;

  2)线程执行发生异常,此时JVM会让线程自动释放锁。

  那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法),获取锁的线程被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,这非常影响程序的执行效率。因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到。

  再举个例子:当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象。但是采用synchronized关键字来实现同步的话,就会导致一个问题:如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程只能等待无法进行读操作。因此就需要一种机制来使得多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock就可以办到。另外,通过Lock可以知道线程有没有成功获取到锁。这个是synchronized无法办到的。

  总结一下,也就是说Lock提供了比synchronized更多的功能。但是要注意以下几点:

  1)Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。Lock是一个接口,通过这个接口可以实现同步访问;

  2)Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。

public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

在Lock接口中,lock()、tryLock()、tryLock(long time, TimeUnit unit)和lockInterruptibly()是用来获取锁的。unLock()方法是用来释放锁的。

  首先lock()方法是平常使用得最多的一个方法,就是用来获取锁。如果锁已被其他线程获取,则进行等待。由于在前面讲到如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。通常使用Lock来进行同步的话,是以下面这种形式去使用的:

Lock lock = ...;
lock.lock();
try{
    //处理任务
}catch(Exception ex){

}finally{
    lock.unlock();   //释放锁
}

  其次tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。通常这样使用:

Lock lock = ...;
if(lock.tryLock()) {
     try{
         //处理任务
     }catch(Exception ex){

     }finally{
         lock.unlock();   //释放锁
     }
}else {
    //如果不能获取锁,则直接做其他事情
}

  lockInterruptibly()方法比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。由于lockInterruptibly()的声明中抛出了异常,所以lock.lockInterruptibly()必须放在try块中或者在调用lockInterruptibly()的方法外声明抛出InterruptedException。因此lockInterruptibly()一般的使用形式如下:

public void method() throws InterruptedException {
    lock.lockInterruptibly();
    try {
     //.....
    }
    finally {
        lock.unlock();
    }
}

当一个线程获取了锁之后,是不会被interrupt()方法中断的。单独调用interrupt()方法不能中断正在运行过程中的线程,只能中断阻塞过程中的线程。因此当通过lockInterruptibly()方法获取某个锁时,如果不能获取到,只有进行等待的情况下,是可以响应中断的。而用synchronized修饰的话,当一个线程处于等待某个锁的状态,是无法被中断的,只有一直等待下去。

  总结:lock( )方法,如果锁已被其他线程获取,则会等待。tryLock( )方法,拿不到锁时不会一直在那等待。lockInterruptibly( )方法能够响应中断。ReentrantLock,意思是“可重入锁”,ReentrantLock是唯一实现了Lock接口的类,并且ReentrantLock提供了更多的方法。常用方法Demo如下。

参考地址:

[1]http://www.cnblogs.com/dolphin0520/p/3923167.html

时间: 2024-10-25 22:03:03

类Condition和类ReentrantLock的使用的相关文章

基类,派生类,内存分配情况?.xml

pre{ line-height:1; color:#1e1e1e; background-color:#d2d2d2; font-size:16px;}.sysFunc{color:#627cf6;font-style:italic;font-weight:bold;} .selfFuc{color:#800080;} .bool{color:#d2576f;} .condition{color:#000080;font-weight:bold;} .key{color:#000080;} .

debug类和trace类的区别

在 .net 类库中有一个 system.diagnostics 命名空间,该命名空间提供了一些与系统进程.事件日志.和性能计数器进行交互的类库.当中包括了两个对开发人员而言十分有用的类——debug类和trace类.本文介绍了这两个类的一些基本用途,旨在提高广大开发人员的开发效率. 使用debug类来帮助调试 调试程序对每个程序员来说是家常便饭.可是我们会经常遇到一些情况让我们头疼,例如: 当我们在开发一个界面控件的时候,简单的设断点会增加paint事件的响应次数,而造成的环境参数改变. 断点

JAVA正则表达式:Pattern类与Matcher类详解(转)

java.util.regex是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包.它包括两个类:Pattern和Matcher Pattern 一个Pattern是一个正则表达式经编译后的表现模式. Matcher 一个Matcher对象是一个状态机器,它依据Pattern对象做为匹配模式对字符串展开匹配检查. 首先一个Pattern实例订制了一个所用语法与PERL的类似的正则表达式经编译后的模式,然后一个Matcher实例在这个给定的Pattern实例的模式控制下进行字符串的匹配工作

【python】-- 类的多继承、经典类、新式类

继承知识点补充 在python还支持多继承,但是一般我们很少用,有些语言干脆就不支持多继承,有多继承,就会带来两个概念,经典类和新式类. 一.多继承 之前我们都是讲的单继承,那么什么是多继承呢?说白了,就是:子类可以继承多个父类,就叫多继承. class SchoolMember(object): #SchoolMember类 '''学校成员基类''' def tell(self): print("the schoolmeber is tell...") class School(ob

JAVA API(一)String类和StringBuffer类

1.String类和StringBuffer类 在程序中经常会用到字符串,所谓的字符串就是指一连串的字符,它是由多个单个字符连接而成的.字符串中可以包含任意字符,这些字符必须包含在一对双引号""之内,如"abc".在Java中定义了String和StringBuffer类来封装字符串,并提供了一系列操作字符串的方法,它们都位于java.lang包中,因此不需要导包就可以直接使用.下面将对String类和StringBuffer类详细讲解. 1.1String类的初始

从设计基类及其派生类看继承关系

继承能够定义可重用.扩展或修改父类行为的子类.但基类的静态构造函数.实例构造函数和析构函数不能被派生类继承. 在下面实例中,定义一个基类Publication用于表示任何类型的出版物以及派生至Publication的其他类型Book类,由此也可以扩展为定义其他类型如:Magazine.Journal.Newspaper和Article. 在设计基类Publication时我们必须考虑到如下关系: 1.要在基类中添加哪些成员 2.基类是否用作派生类模板的抽象基类 3.类层次结构的扩展空间大小,要开

派生类和基类的转换

指针引用分四种情况: 1.直接用基类指针引用基类对象 2.直接用派生指针引用派生对象 3.由基类指针引用派生类对象,由于派生类也是基类对象(包含关系),所以这种引用是安全的.但是只能引用基类成员,若试图通过基类指针引用那些只在派生类中才有的成员,编译器会报告语法错误.(解决该问题的答案是虚函数和多态性) 4.用派生类指针引用基类对象,这种方式会导致编译器报错.必须先把派生类指针强制转换成基类指针. 如果基类和派生类都定义了同名函数,通过对象指针调用成员函数时,到底调用哪里的函数由指针的原始类型决

OC学习篇之---Foundation框架中的NSDictionary类以及NSMutableDictionary类

今天来看一下Foundation框架中的NSDictionary类,NSMutableDictionary类,这个和Java中的Map类很想,OC中叫字典,Java中叫Map,还有字典是无序的,这个和NSArray不一样,Java中的Map也是无序的,通过hash值去检索元素的. 一.NSDictionary类 [objc] view plain copy // //  main.m //  19_NSDictionary // //  Created by jiangwei on 14-10-

java基础,继承类题目:编写一个Java应用程序,该程序包括3个类:Monkey类、People类和主类 E

21.编写一个Java应用程序,该程序包括3个类:Monkey类.People类和主类 E.要求: (1) Monkey类中有个构造方法:Monkey (String s),并且有个public void speak() 方法,在speak方法中输出“咿咿呀呀......”的信息. (2)People类是Monkey类的子类,在People类中重写方法speak(),在speak方法 中输出“小样的,不错嘛!会说话了!”的信息. (3)在People类中新增方法void think(),在thi