java多线程总结-同步之ReentrantLock

1 ReentrantLock与synchronized对比

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

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(),有两种方式this

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

    boolean tryLock();线程

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

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

/**
 * 尝试锁
 */
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唤醒。没法打断
锁池队列: 没法获取锁标记。不是全部的锁池队列均可被打断blog

  • 使用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");
            }
        }
    }
}

公平锁结果:

非公平锁结果:

相关文章
相关标签/搜索