java高并发之——synchronized和重入锁(ReentrantLock)

一、重入锁的定义:

为什么会叫重入锁,顾名思义,表示这个锁可以返回被添加,就是一个线程可以多次获得一把锁,只要在最后的时候做相同次数的锁释放即可。

Lock lock = new ReentrantLock();
lock.lock();
lock.lock();
try {
   //业务代码
} finally {
   lock.unlock();
   lock.unlock();
}

二、重入锁和synchronized的区别

首先java在一开始时,可以用synchronized来解决并发过程中数据安全问题,那为什么还需要使用Lock锁呢?有的人可能会认为是性能问题:认为ReentrantLock锁的性能优于synchronized。其实这种想法是错误的,确实在JDK6.0之前,synchronized的性能是远不如重入锁的性能的,但在JDK6.0版本对synchronized做了大量的优化,使得两者的性能差异并不大。所有java为什么会重复造轮子,引入重入锁呢?其重要原因有三点:

1.支持中断响应:对于synchronized来说,一个线程如果在等待获得锁资源,那么只有两种可能,获得这个锁资源继续向下执行,或者没有得到锁资源继续等待下去。但是重入锁,给我提供了另外一种机制,那就是线程在等待的时候,可以根据需求取消对锁的请求。这样就可以很好的解决线程的死锁问题:例如一个线程(Thread-1)在获得锁A的资源后,当再去获得锁B的资源时,没有获得锁B的资源,从而进入等待状态,但这时这个线程得到的锁A资源不释放,如果这时另一个线程(Thread-2)已经获得了锁B,去请求锁A时,请求不到资源,这时就会进入死锁状态。如果使用的是重入锁,就可以给线程(Thread-1或者Thread-2)一个中断响应,那么这个线程对应的获得的锁资源也会被释放掉,从而避免死锁的发生。

public class IntLock implements Runnable {
    public static ReentrantLock lock1 = new ReentrantLock();
    public static ReentrantLock lock2 = new ReentrantLock();
    int lock;

    public IntLock(int lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        try {
            //产生死锁
            if (lock == 1) {
                lock1.lockInterruptibly();//可以对中断进行响应的加锁方式
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {

                }
                lock2.lockInterruptibly();
            } else {
                lock2.lockInterruptibly();
                Thread.sleep(500);
                lock1.lockInterruptibly();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (lock1.isHeldByCurrentThread()) {
                lock1.unlock();
            }
            if (lock2.isHeldByCurrentThread()) {
                lock2.unlock();
            }
            System.out.println(Thread.currentThread().getId()+":线程退出");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        IntLock r1 = new IntLock(1);
        IntLock r2 = new IntLock(2);
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);
        t1.start();
        t2.start();
        //已经死锁
        Thread.sleep(1000);
        //通过中断其中一个线程,使其释放获得锁资源,从而结束死锁
        t2.interrupt();
    }

2.支持超时:一个线程在请求锁资源时,可以设置一个超时时间,一但超过这个超时时间,线程不会一直处在等待状态,而是抛出一个异常,并可以释放掉曾经持有的资源。

// 支持超时的 API
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

代码实例:

public class IntLock implements Runnable {
    public static ReentrantLock lock1 = new ReentrantLock();

    @Override
    public void run() {
        try {
            if (lock1.tryLock(5, TimeUnit.SECONDS)) {
                Thread.sleep(6000);
            } else {
                System.out.println("获得锁超时");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (lock1.isHeldByCurrentThread()) {
                lock1.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        IntLock l1 = new IntLock();
        Thread t1 = new Thread(l1);
        Thread t2 = new Thread(l1);
        t1.start();
        t2.start();

    }
}

3.公平性问题:使用synchronized的加锁方式,就是一种非公平锁,也就是当一个线程释放掉自己的锁资源后。系统只是从等待的线程中,随机的选取一个执行,这样做的结果也就是会产生饥饿现象,也就是锁某个线程会一直得不多锁资源从而一直等待。但是重入锁,可以设置其属性,他有一个构造方法

public ReentrantLock(boolean fair);

其中:设置为true,表示使用公平锁,false使用非公平锁。但是使用公平锁时,需要额外维护一个有序队列,从而降低性能,因此相对想来,没有特别需求,一般都是使用的非公平锁。

4.非阻塞的获得锁:当一个线程在没有获得锁资源时,不是进入等待,而是直接返回结果,那这个线程也会释放掉曾经得到的资源。

// 支持非阻塞获取锁的 API
boolean tryLock();

代码实例:

public class IntLock implements Runnable {
    public static ReentrantLock lock1 = new ReentrantLock();

    @Override
    public void run() {
        try {
            if (lock1.tryLock()){
                Thread.sleep(100);
            } else {
                System.out.println("没有获得锁");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (lock1.isHeldByCurrentThread()) {
                lock1.unlock();
            }
        }

    }

    public static void main(String[] args) throws InterruptedException {
        IntLock l1 = new IntLock();
        Thread t1 = new Thread(l1);
        Thread t2 = new Thread(l1);
        t1.start();
        t2.start();

    }

 

你可能感兴趣的:(多线程)