java多线程学习 ReentrantLock类的使用

ReentrantLock类

为了保证任何时刻只有一个线程能进入临界区,通常需要给临界区上锁,只有获得锁的线程才能进入临界区。为了达到上锁的目的,我们通常使用synchronized关键字。
在Java SE 5.0之后,java引入了一个ReentrantLock类,也可以实现给代码块上锁和释放锁的效果。

lock方法 和unlock方法

  • lock() 申请获得锁
    • 如果获得锁,该线程可以继续往下执行
    • 如果该锁已被其他线程获取,当前线程停止运行并进入阻塞状态,等待其他线程释放锁
  • unlock() 释放锁
  • ReentrantLock和synchronized锁一样是可重入锁,
    • 一个线程可以重复的获得已经持有的锁,锁保持一个持有计数(holdcount) 来跟踪对 lock 方法的嵌套调用。
    • 线程在每一次调用 lock 都要调用 unlock 来释放锁。
    • 由于这一特性, 被一个可重入锁保护的代码可以调用另一个使用相同的锁的方法。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class myThread9 {
    //ReentrantLock类实现了Lock接口
    private static final Lock lock = new ReentrantLock();
    private int count = 0;

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public void add() {
        lock.lock();//申请获得锁
        try {
            //一般为了安全,一般用try把同步代码块的内容包裹起来
            //保证即使当前线程运行过程中出现异常,也能正常释放锁
            for (int i = 0; i < 10000; i++) {
                count++;
            }
        } finally {
            //一般在finally里释放锁
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        myThread9 m = new myThread9();
        List<Thread> threads = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            //新建10个线程,加入threads中
            Thread thread = new Thread(() -> {
                m.add();
            }, "thread-" + i);
            threads.add(thread);
        }
        //逐一启动线程
        threads.forEach((thread -> thread.start()));
        //在主线程中利用join方法,等待所有方法完成
        threads.forEach(thread -> {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        //所有子线程运行结束,打印count值
        System.out.println(m.getCount());
    }
}

java多线程学习 ReentrantLock类的使用_第1张图片

trylock方法(限时等待)

  • trylock():尝试获得锁,如果获得锁,返回true.如果没有获得锁,返回false(当前线程不会进入阻塞状态)
  • trylock(long time,TimeUnit unit):在指定时间时间内尝试获得锁,如果成功获取锁,返回true,如果没有锁,返回false(当前线程不会进入阻塞状态)
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class myThread9_1{
    //ReentrantLock类实现了Lock接口
    private static final Lock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        myThread9_1 m = new myThread9_1();
        //启动第一个线程,执行add方法
        new Thread(()->m.add(),"t1").start();
        //过1s后启动第二个线程执行testTryLock,确保第一个线程能先启动获得锁
        Thread.sleep(1000);
        new Thread(()->m.testTryLock(),"t2").start();
    }

    public void add(){
        lock.lock();
        try {
            //该线程获得锁后需要5s才能运行完毕释放锁
        for (int i = 0; i <5 ; i++) {
            System.out.println(Thread.currentThread().getName()+":当前i的值为"+i);
            Thread.sleep(1000);
        }}catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void testTryLock(){
        boolean flag=false;
        try {
            //可以尝试不同等待时间对比运行结果
            flag = lock.tryLock();//尝试立即获得锁
 //          flag = lock.tryLock(2,TimeUnit.SECONDS);//等待2s,尝试获得锁
//           flag = lock.tryLock(6,TimeUnit.SECONDS);//等待6s,尝试获得锁
            if (flag){
                //获得锁
                System.out.println(Thread.currentThread().getName()+"获得锁,执行同步代码块里内容");
            }else {
                System.out.println(Thread.currentThread().getName()+"没有在规定时间内获得锁,执行非同步代码块内容");
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (flag){
                //如过获得锁,要释放锁
                lock.unlock();
            }
        }
    }
}

java多线程学习 ReentrantLock类的使用_第2张图片

公平锁

含义:谁等的时间最长,谁就先获取锁。
默认情况下,ReentrantLock是非公平锁,可以通过构造方法参数把ReentrantLock声明为公平锁

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

public class myThread9_2 {
    //在new ReentrantLock时传入true就可以把lock设置为公平锁(默认时false)
    private static final Lock lock = new ReentrantLock(true);

    public void test() {
        for (int i = 0; i < 3; i++) {
            try {
                lock.lock();
                System.out.println(Thread.currentThread().getName() + "获得锁");
                Thread.sleep(1000);

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        myThread9_2 m = new myThread9_2();
        for (int i = 1; i <= 5; i++) {
            //m::test等价于()->m.test();
            new Thread(m::test, "线程" + i).start();
        }

    }
}

java多线程学习 ReentrantLock类的使用_第3张图片
java多线程学习 ReentrantLock类的使用_第4张图片

ReentrantLock和synchronized的对比

  • ReentrantLock和synchronized都是独占锁(排他锁),一个时刻都只能有一个线程持有该锁
  • ReentrantLock和synchronized都是可重入锁,一个线程可以重复的获得已经持有的锁
  • synchronized可以自动获得锁和释放锁,ReentrantLock需要手动获得锁并手动在finally里释放锁
  • 在性能上,ReentrantLock是轻量级锁;但jdk 1.5之后,synchronized加入了锁升级的概念(偏向锁,轻量级锁,重量级锁),所以二者性能相当.
  • synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以响应中断

你可能感兴趣的:(java学习,多线程)