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. List集合去重方式及效率对比

    List集合相信大家在开发过程中几乎都会用到.有时候难免会遇到集合里的数据是重复的,需要进行去除.然而,去重方式有好几种方式,你用的是哪种方式呢?去重方式效率是否是最高效.最优的呢?今天就给大家讲解一 ...

  2. 图像滤镜艺术---PS图像转手绘特效实现方案

    原文:图像滤镜艺术---PS图像转手绘特效实现方案 手绘效果实现方案 本文介绍一种PS手绘效果的实现方案,PS步骤来自网络,本文介绍代码实现过程. 整体看来,虽然效果还是有很大差异,但是已经有了这种特 ...

  3. 图像滤镜艺术--编码基础(Photoshop基础变换的代码实现)

    原文:图像滤镜艺术--编码基础(Photoshop基础变换的代码实现) 自从上一篇博客写完之后,到现在已经有段时间了,这段时间不是不想接着写,只是想做的更好了在写出来给大家看呵呵. 今天,我将给大家介 ...

  4. Interactive Data Display如何显示菜单?

    貌似直接跟其它控件一样添加菜单就行了. 不过这个版本已经取消了默认菜单,所有的菜单功能都得自己去实现.

  5. 利用NPOI生成DOCX文档

    首先安装NPOI控件: Install-Package NPOI 代码: using NPOI.OpenXmlFormats.Wordprocessing; using NPOI.XWPF.UserM ...

  6. Database Comparer VCL 6.4.908.0 D5-XE10.1

    Database Comparer VCL compares and synchronizes databases structure (metadata) and table data for ma ...

  7. C#图片处理之:旋转图片90度的整数倍

    原文:C#图片处理之:旋转图片90度的整数倍 旋转图片90的整数倍那真是太简单了.         public static Bitmap KiRotate90(Bitmap img)        ...

  8. HTTP通信过程原理

    HTTP协议 通信过程介绍 HTTP协议介绍 Http(Hypertext Transfer Protocol)超文本传输协议. Http是应用层协议,当你上网浏览网页的时候,浏览器和服务器之间就会通 ...

  9. Linux下C/C++帮助手册安装方法

    1.  安装C的帮助手册 如果你使用的Linux发行版, 默认没有安装C语言的库函数MAN手册, 使用下面的方法解决: # sudo apt-get install manpages # sudo a ...

  10. 可视化文件消息收发一体化Socket实现V0.1

    http://blog.csdn.net/laoyang360/article/details/8681918