1 ReentrantLock与synchronized对比

ReentrantLock与synchronized都是为了同步加锁,但ReentrantLock相对效率比synchronized高,量级较轻。
synchronized在JDK1.5版本开始,尝试优化。到JDK1.7版本后,优化效率已经非常好了。在绝对效率上,不比reentrantLock差多少。使用ReentrantLock,必须手工释放锁标记。一般都是在finally代码块中定义释放锁标记的unlock方法。

2.示例用法

2.1 基本用法

lock()与unlock()就像synchronized同步代码块的开始与结束,使用ReentrantLocky一定要记得unlock()解锁

package com.bernardlowe.concurrent.t03;

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

public class Test_01 {
    Lock lock = new ReentrantLock();

    void m1(){
        try{
            lock.lock(); // 加锁
            for(int i = 0; i < 10; i++){
                TimeUnit.SECONDS.sleep(1);
                System.out.println("m1() method " + i);
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            lock.unlock(); // 解锁
        }
    }

    void m2(){
        lock.lock();
        System.out.println("m2() method");
        lock.unlock();
    }

    public static void main(String[] args) {
        final Test_01 t = new Test_01();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.m1();
            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.m2();
            }
        }).start();
    }
}

2.2 尝试锁

尝试锁,顾名思义是尝试获取锁标记trylock(),有两种方式

  • 无参尝试锁:会根据是否能获取当前锁标记返回对应值

    boolean tryLock();

  • 有参阻塞尝试锁, 阻塞尝试锁,阻塞参数代表等待超时时间。

    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

/**
 * 尝试锁
 */
package com.bernardlowe.concurrent.t03;

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

public class Test_02 {
    Lock lock = new ReentrantLock();

    void m1(){
        try{
            lock.lock();
            for(int i = 0; i < 10; i++){
                TimeUnit.SECONDS.sleep(1);
                System.out.println("m1() method " + i);
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }

    void m2(){
        boolean isLocked = false;
        try{
            // 尝试锁, 如果有锁,无法获取锁标记,返回false。
            // 如果获取锁标记,返回true
            // isLocked = lock.tryLock();

            // 阻塞尝试锁,阻塞参数代表的时长,尝试获取锁标记。
            // 如果超时,不等待。直接返回。
            isLocked = lock.tryLock(5, TimeUnit.SECONDS); 

            if(isLocked){
                System.out.println("m2() method synchronized");
            }else{
                System.out.println("m2() method unsynchronized");
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            if(isLocked){
                // 尝试锁在解除锁标记的时候,一定要判断是否获取到锁标记。
                // 如果当前线程没有获取到锁标记,会抛出异常。
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        final Test_02 t = new Test_02();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.m1();
            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.m2();
            }
        }).start();
    }
}

2.3 可打断

先解释下线程的几种状态:
阻塞状态: 包括普通阻塞,等待队列,锁池队列。
普通阻塞: sleep(10000), 可以被打断。调用thread.interrupt()方法,可以打断阻塞状态,抛出异常。
等待队列: wait()方法被调用,也是一种阻塞状态,只能由notify唤醒。无法打断
锁池队列: 无法获取锁标记。不是所有的锁池队列都可被打断

  • 使用ReentrantLock的lock方法,获取锁标记的时候,如果需要阻塞等待锁标记,无法被打断。
  • 使用ReentrantLock的lockInterruptibly方法,获取锁标记的时候,如果需要阻塞等待,可以被打断。

示例代码
主线程启动了两个线程t1,t2,t1调用m1(),t2调用m2()
当主线程调用interrupt()方法,可以打断t2线程的阻塞等待锁,抛出异常

package com.bernardlowe.concurrent.t03;

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

public class Test_03 {
    Lock lock = new ReentrantLock();

    void m1(){
        try{
            lock.lock();
            for(int i = 0; i < 5; i++){
                TimeUnit.SECONDS.sleep(1);
                System.out.println("m1() method " + i);
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }

    void m2(){
        try{
            lock.lockInterruptibly(); // 可尝试打断,阻塞等待锁。可以被其他的线程打断阻塞状态
            System.out.println("m2() method");
        }catch(InterruptedException e){
            System.out.println("m2() method interrupted");
        }finally{
            try{
                lock.unlock();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        final Test_03 t = new Test_03();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.m1();
            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                t.m2();
            }
        });
        t2.start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        t2.interrupt();// 打断线程休眠。非正常结束阻塞状态的线程,都会抛出异常。
    }
}

结果如图

2.4 公平锁

操作系统cpu,为了保证效率,线程的执行机制是竞争机制,或者说是随机机制,是不公平的,使用ReentrantLock实现公平锁,是非常简单的,只需要在创建ReentrantLock的时候传一个参数ReentrantLock lock = new ReentrantLock(true);
示例代码:
TestReentrantlock是公平锁
TestSync是非公平锁

/**
 * 公平锁
 */
package com.bernardlowe.concurrent.t03;

import java.util.concurrent.locks.ReentrantLock;

public class Test_04 {

    public static void main(String[] args) {
        TestReentrantlock t = new TestReentrantlock();
        //TestSync t = new TestSync();
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        t1.start();
        t2.start();
    }
}

class TestReentrantlock extends Thread{
    // 定义一个公平锁
    private static ReentrantLock lock = new ReentrantLock(true);
    public void run(){
        for(int i = 0; i < 5; i++){
            lock.lock();
            try{
                System.out.println(Thread.currentThread().getName() + " get lock");
            }finally{
                lock.unlock();
            }
        }
    }

}

class TestSync extends Thread{
    public void run(){
        for(int i = 0; i < 5; i++){
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + " get lock in TestSync");
            }
        }
    }
}

公平锁结果:

非公平锁结果:

多线程总结-同步之ReentrantLock的更多相关文章

  1. JAVA - 多线程的同步

    多线程的同步 1. 锁对象. 应用场景:当某个数据可能被其他线程修改时,给涉及到数据的方法上锁,保证同一时刻只有拥有这个锁的线程能访问该数据,其他要调用这个方法的线程被阻塞.注意:必须是不同线程访问同 ...

  2. java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析

    java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...

  3. Java多线程的同步控制记录

    Java多线程的同步控制记录 一.重入锁 重入锁完全可以代替 synchronized 关键字.在JDK 1.5 早期版本,重入锁的性能优于 synchronized.JDK 1.6 开始,对于 sy ...

  4. Java多线程之同步集合和并发集合

    Java多线程之同步集合和并发集合 不管是同步集合还是并发集合他们都支持线程安全,他们之间主要的区别体现在性能和可扩展性,还有他们如何实现的线程安全. 同步集合类 Hashtable Vector 同 ...

  5. Java基础知识笔记(五:多线程的同步问题)

    编写多线程程序往往是为了提高资源的利用率,或者提高程序的运行效率,或者更好地监控程序的运行过程等.多线程同步处理的目的是为了让多个线程协调地并发工作.对多线程进行同步处理可以通过同步方法和同步语句块实 ...

  6. 转载自~浮云比翼:Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)

    Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)   介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可 ...

  7. Step by Step:Linux C多线程编程入门(基本API及多线程的同步与互斥)

    介绍:什么是线程,线程的优点是什么 线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,同一进程中的多条线程将共享该进程中的全部系统资源,如虚拟地址空间, ...

  8. Python标准库08 多线程与同步 (threading包)

    Python主要通过标准库中的threading包来实现多线程.在当今网络时代,每个服务器都会接收到大量的请求.服务器可以利用多线程的方式来处理这些请求,以提高对网络端口的读写效率.Python是一种 ...

  9. 【转】【玩转cocos2d-x之二十三】多线程和同步03-图片异步加载

    原创作品,转载请标明:http://blog.csdn.net/jackystudio/article/details/15334159 cocos2d-x中和Android,Windows都 一样, ...

随机推荐

  1. WPF中的资源(二) - 二进制资源

    原文:WPF中的资源(二) - 二进制资源 WPF中的二进制资源,就是类似于MFC中在对话框程序中添加的图片.字符串等资源,程序在运行时将其转换成二进制,以供程序使用. 下面以将字符串转换成二进制为例 ...

  2. 【Gerrit】自动设置reviewer

    gerrit 提供了一种代码review解决方案,但每次代码提交之后都要设置每个commit的code reviewer, 实在是痛苦. gerrit 在官方说明文档里面提供了解决方法,地址:http ...

  3. Win10《芒果TV》送7天免费会员,邀您抢先看萌心自制《妈妈是超人3》

    <妈妈是超人>第三季萌心归来,霍思燕,贾静雯,黄圣依,邓莎联合释放"妈妈的声音",嗯哼,咘咘,波妞,安迪,大麟子五位萌娃共同出镜,萌化屏幕.Win10<芒果TV& ...

  4. 使用NEWSEQUENTIALID解决GUID聚集索引问题

    原文:使用NEWSEQUENTIALID解决GUID聚集索引问题 UNIQUEIDENTIFIER做主键(Primary Key)是一件很方便的事情,在数据合并等操作中有不可替代的优势 但是由于普通的 ...

  5. <input type="image"> 和 <img> 用法区别

    原文:<input type="image"> 和 <img> 用法区别 w3c定义如下: Image <input type="image ...

  6. JS function document.onclick(){}报错Syntax error on token "function", delete this token - CSDN博客

    原文:JS function document.onclick(){}报错Syntax error on token "function", delete this token - ...

  7. 用CDialog实现的消息框MessageBoxST类

    http://blog.csdn.net/akof1314/article/details/5078563

  8. Natively Compiled Code: A Comeback?

    RAD Studio and Natively Compiled Code In today's development landscape, natively compiled code is ma ...

  9. Web页面制作之开发调试工具

    直击现场 <HTML开发MacOSApp教程>  http://pan.baidu.com/s/1jG1Q58M 开发工具介绍 开发工具一般分为两种类型:文本编辑器和集成开发环境(IDE) ...

  10. Windows完成端口编程

    Windows完成端口编程目录一 基本概念二 OVERLAPPED数据结构三 完成端口的内部机制创建完成端口完成端口线程的工作原理线程间数据传递线程的安全退出 一 基本概念       设备---wi ...