Lock体系(JDK 1.5)

1、解决死锁相较于synchronized独有的方法(特性)

  • 响应中断(破坏不可抢占)
    void lockInterruptibly() throws InterruptedException;

当调用Interrupt()方法,会抛出一个中断异常,在catch块中捕获这个异常,就会终止线程,释放资源。

class LockTaskOne implements Runnable {
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        try {
            while (true) {
            //lockInterruptibly()尝试获取锁
                lock.lockInterruptibly();
            }
        } catch (Exception e) {
            System.out.println("进入catch块,线程终止");
            return;
        } finally {
            lock.unlock();
        }
    }
}

public class LockInterrupt {
    public static void main(String[] args) throws InterruptedException {
        LockTaskOne taskOne = new LockTaskOne();
        Thread thread = new Thread(taskOne);
        thread.start();
        TimeUnit.SECONDS.sleep(2);
        thread.interrupt();
    }
}

在这里插入图片描述

  • 非阻塞式获取锁(破坏占有且等待)
    boolean tryLock();

tryLock()尝试获取锁,如果获取成功,进入同步代码块,如果获取失败,不等待继续往下走。
synchronized是阻塞式获取锁,如果获取失败,就一直卡着。

class LockTaskTwo implements Runnable {
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        boolean isLock = false;
        try {
            if (lock.tryLock()) {
                System.out.println(Thread.currentThread().getName() +
                        "获取锁成功");
                TimeUnit.SECONDS.sleep(1);
                isLock = true;
            } else {
                System.out.println("获取锁失败,线程继续运行");
            }
        } catch (Exception e) {

        } finally {
            if (isLock) {
                lock.unlock();
            }
        }
    }
}

public class TryLockTest {
    public static void main(String[] args) {
        LockTaskTwo taskTwo = new LockTaskTwo();
        Thread threadA = new Thread(taskTwo, "线程A");
        Thread threadB = new Thread(taskTwo, "线程B");
        threadA.start();
        threadB.start();
    }
}

在这里插入图片描述

  • 支持超时(对所有条件都进行破坏)
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
class LockTaskTwo implements Runnable {
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        boolean isLock = false;
        try {
            if (lock.tryLock(2, TimeUnit.SECONDS)) {
                System.out.println(Thread.currentThread().getName() +
                        "获取锁成功");
                TimeUnit.SECONDS.sleep(1);
                isLock = true;
            } else {
                System.out.println("获取锁失败,线程继续运行");
            }
        } catch (Exception e) {

        } finally {
            if (isLock) {
                lock.unlock();
            }
        }
    }
}

public class TryLockTest {
    public static void main(String[] args) {
        LockTaskTwo taskTwo = new LockTaskTwo();
        Thread threadA = new Thread(taskTwo, "线程A");
        Thread threadB = new Thread(taskTwo, "线程B");
        threadA.start();
        threadB.start();
    }
}

在这里插入图片描述

这个方法是尝试获取锁,如果失败,等待一段时间,如果在这段时间内获取到锁,就进入同步代码块继续执行,否则线程退出,继续执行。

2、lock的使用格式

try {
	// 同步代码块(临界区)
	lock.lock();
}catch(Exception e) {
	
}finally {
	// 显示解锁
	lock.unlock();
}

3、Lock接口的子类

3.1 ReentrantLock(80%):可重入锁,使用与synchronized基本一致
  • 使用ReentrantLock进行同步处理
class Task implements Runnable {
    private int ticket = 20;
    private Lock lock = new ReentrantLock();

    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                TimeUnit.MILLISECONDS.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                lock.lock();
                //需要加锁,临界区代码
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() +
                            "还剩下" + ticket-- + "票");
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

public class TicketTest {
    public static void main(String[] args) {
        Task task = new Task();
        new Thread(task, "黄牛A").start();
        new Thread(task, "黄牛B").start();
        new Thread(task, "黄牛C").start();
    }
}

synchronized与ReentrantLock的关系与区别?

  • 共性:
    1) 都属于独占锁的实现,任意一个时刻只有一个线程能够获取到锁。
    2)都支持可重入。
  • 区别:
    1)synchronized属于JVM层面的管程实现(是C语言实现的,是一个关键字),ReentrantLock属于Java语言层面实现的管程。
    2)ReentrantLock有一些synchronized不具备的特性:响应中断,支持超时,支持非阻塞式的获取锁,还可以实现公平锁,支持多个等待队列(synchronized只有一个等待队列)。

公平锁:等待时间最长的线程最先获取锁。
任何一个对象都存在两个队列:
同步队列:所有获取该对象Monitor失败的线程进入同步队列
等待队列:调用wait()阻塞的线程进入等待队列

假设现在有5个线程,Thread0,Thread1,Thread2,Thread3,Thread4,Thread0首先获取到Monitor,其它几个线程就会进入同步队列,这时Thread0再调用wait方法阻塞后就会进入等待队列,按理说Thread1可以获得Monitor了,但是如果此时又新开了两个线程Thread5,Thread6,那么肯定是Thread5或者Thread6先获取到Monitor,因为这俩处于运行态,所以说synchronized是一个非公平锁。

ReentrantLock默认是非公平锁,但是它可以传一个参数:

public ReentrantLock(boolean fair) {
	sync = fair ? new FairSync() : new NonfairSync() ;
}

ReentrantLock可以保证,不管当前有几个线程,只要它在同步队列的队首,只要monitor释放,就一定可以获得锁。

3.2 ReentrantReadWriteLock:读写锁(读者写者模型)

应用:共享单车
App中显示可以用的车辆,不解锁的时候多个人都可以看到这辆车,那么每个人都是一个读锁,当有任何一个人预约之后,预约就是写锁,其他人不能再预约,也就是说,有人骑走之后,APP上就不再显示这辆车,即当有人获取到写锁,读锁也会阻塞(看不到车)。

读者写者模型中,读读异步,读写、写写同步

  • 读锁:共享锁,任意一个时刻可以有多个不同线程获取锁
  • 写锁:独占锁,任意一个时刻只能有一个线程获取写锁

ReentrantReadWriteLock实现了读写锁(可重入读写锁)

  • readLock():获取读锁
  • writerLock():获取写锁

应用:使用HashMap+ReentrantReadWriteLock实现多线程缓存

public class Cache {
    private Map<String, String> map = new HashMap<>();
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public String getValue(String key) {
        //获取到读锁
        lock.readLock();
        return map.get(key);
    }

    public void setValue(String key, String value) {
        //获取到写锁
        lock.writeLock();
        map.put(key, value);
    }
}

4、Lock接口中最后一个方法newCondition()

Condition newCondition():每调用一次就产生一个新的Condition(等待队列)对象。
Condition里的await(),signal()方法与wait(),notify()方法用法一样。
区别在于:
原来的wait(),notify()唤醒的是一个队列中的线程,而现在的await(),signal()唤醒的是自己队列中的线程,当前通过哪个condition调用,它就唤醒的是哪个队列中的线程。
应用:使用Lock+Condition实现多生产者消费者模型
这个在我的另一篇文章里,附上链接:
https://blog.csdn.net/Nan_Feng726/article/details/98601732

synchronized与Lock的关系与区别:
1)都可以实现独占锁的实现,都支持可重入的特性。
2)Lock具备一些synchronized不具备特性,响应中断,支持超时,支持非阻塞式获取锁,还可以实现公平锁,支持多个等待队列,支持读写锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值