ReentrantLock(重入锁)

ReentrantLock

先看一段程序:

public class DemoT {
    //    定义一个共享变量
    private static int count = 0;

    //  用来操作共享变量的方法
    public static void inc() {
        try {
//           为了展示出多线程同时操作同一数据会出现问题。如果去掉之后可能会正常计算出数据
            Thread.sleep(1);
            count++;
//            decr();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
    }


    public static void main(String[] args) throws InterruptedException {
//        新建三个线程 每个循环100遍,去调用inc方法操作共享变量
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread.start();
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread2.start();
        Thread thread3 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread3.start();
//        保证三个子线程已经运行结束
        Thread.sleep(2000);
//        打印共享变量
        System.out.println("count:" + count);
    }
}

想要的结果:(打印:count:300)

实际打印结果:count:285(可能不是285,但是基本上都是小于300)

添加重入锁(ReentrantLock)(还是之前的代码只不过加了一把锁)

public class DemoT {
    //    定义一个共享变量
    private static int count = 0;

    //添加重入锁   @param fair {@code true} if this lock should use a fair ordering policy(译:@param fair{@code true}如果此锁应使用公平排序策略)
//    如果添加true 则是使用公平锁,否则为非公平锁
    static Lock lock = new ReentrantLock(true);


    //  用来操作共享变量的方法
    public static void inc() {
        lock.lock(); //获得锁(互斥锁)  
        try {
//           为了展示出多线程同时操作同一数据会出现问题。如果去掉之后可能会正常计算出数据
            Thread.sleep(1);
            count++;
//            decr();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//释放锁
        }
    }


    public static void main(String[] args) throws InterruptedException {
//        新建三个线程 每个循环100遍,去调用inc方法操作共享变量
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread.start();
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread2.start();
        Thread thread3 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread3.start();
//        保证三个子线程已经运行结束
        Thread.sleep(2000);
//        打印共享变量
        System.out.println("count:" + count);
    }
}

运行得到:count:300

实现想要的结果,所以这是一个互斥锁,只允许一个线程进入当前范围

那为什么他叫“重入”呢?是因为他可以获得当前锁的情况下,还可以进入另外一个以当前锁为锁头的代码块。例如下面代码:(还是之前代码,只不过添加了一个方法体用来对共享变量递减)


public class DemoT {
    //    定义一个共享变量
    private static int count = 0;

    //添加重入锁   @param fair {@code true} if this lock should use a fair ordering policy(译:@param fair{@code true}如果此锁应使用公平排序策略)
//    如果添加true 则是使用公平锁,否则为非公平锁
    static Lock lock = new ReentrantLock(true);


    //  用来操作共享变量的方法
    public static void inc() {
        lock.lock(); //获得锁(互斥锁)  
        try {
//           为了展示出多线程同时操作同一数据会出现问题。如果去掉之后可能会正常计算出数据
            Thread.sleep(1);
            count++;
            decr();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//释放锁
        }
    }

    public static void decr() {
        lock.lock(); //state=2   //ThreadA再次来抢占锁 : 不需要再次抢占锁,而是只增加重入的次数
        try {
            count--;
        } finally {
            lock.unlock(); //state=1
        }
    }

    public static void main(String[] args) throws InterruptedException {
//        新建三个线程 每个循环100遍,去调用inc方法操作共享变量
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread.start();
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread2.start();
        Thread thread3 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread3.start();
//        保证三个子线程已经运行结束
        Thread.sleep(2000);
//        打印共享变量
        System.out.println("count:" + count);
    }
}

预计结果:count:0

实际运行结果:count:0

概念

重入是如何实现的?

内部有一个 volatile 修饰的变量,当当前线程添加一把锁变量+1 ,当变量结束一个锁,则-1;当为0 说明当前线程无当前锁
 volatile int waitStatus;

没有获取到当前锁的线程怎么操作?以及是怎么保存的?

会放在一个双向链表 使用 addWaiter 将未获得锁的线程加入到队列 

并且区分公平锁和非公平锁

非公平锁:如果当前线程执行到锁的位置,直接去抢占锁,如果抢到,直接拿到锁,运行。链表队列中继续等待。

公平锁:如果当前线程执行到锁的位置,先去链表中判断是否存在其他线程,如果存在,加入队列末尾。如果不存在,去判断锁是否被其他线程拿到,如果有,添加到链表队列,没有执行代码块。

 

你可能感兴趣的:(锁,java,安全,java,thread,安全)