多线程Lock锁

会有一股神秘感。 提交于 2019-12-09 11:01:49

  在JDK1.5以后,在并发包(java.util.concurrent)里面添加包locks,并提供了Lock接口,用于与synchronized类似的锁功能,不同的是Lock需要手动开启锁和释放锁。

 

为什么要用Lock锁?

  1. 尝试非阻塞的获取锁
  2. 获取锁的过程可以被中断
  3. 超时获取锁

 

Lock锁的实现类图

 

Lock锁的常用API

lock():加锁

lockInterruptibly():可中断

tryLock():尝试非阻塞的获取锁

unlock():释放锁

public class AttemptLocking {
    private ReentrantLock lock = new ReentrantLock();
    public void untimed() {
        boolean captured = lock.tryLock();
        try {
            System.out.println("tryLock(): " + captured);
        } finally {
            if(captured)
                lock.unlock();
        }
    }
    public void timed() {
        boolean captured = false;
        try {
            captured = lock.tryLock(2, TimeUnit.SECONDS);
        } catch(InterruptedException e) {
            throw new RuntimeException(e);
        }
        try {
            System.out.println("tryLock(2, TimeUnit.SECONDS): " +
                captured);
        } finally {
            if(captured)
                lock.unlock();
        }
    }
    public static void main(String[] args) {
        final AttemptLocking al = new AttemptLocking();
        al.untimed();
        al.timed();
        new Thread() {
            { setDaemon(true); }
            public void run() {
                al.lock.lock();
                System.out.println("acquired");
            }
        }.start();
        Thread.yield();
        al.untimed();
        al.timed();
    }
}

 

 在untimed方法里如果tryLock()无法拿到锁则离开去做别的事情;在timed方法里设置了等待时间为2秒,如果等待2秒还没拿到锁则离开去做别的事情。

 

Lock锁使用模板

Lock lock = new ReentrantLock();
lock.lock();
try {
  // do my work
} finally {
  lock.unlock();
}

注:这里lock()方法不能写到try里,因为如果自定义的lock发生异常,会导致空放解锁。return要放到try里面,以保证unlock()不会过早发生,从而将数据暴露给了第二个任务。

 

Lock锁的可重入性

  可重入性是指当前线程可以重复进入被自己锁的代码块。如果没有可重入性,当遇到递归操作时就会发生死锁现象。

 

公平锁和非公平锁

  • 公平锁:在多个线程等待锁的时候,当锁被释放时,保证最先请求的线程先得到锁(也就是等待时间最长的线程),保证了每个线程都可以得到锁。
  • 非公平锁:与公平锁不一样的是,非公平锁不敢保证每个线程都可以拿到锁,但是可以提高线程交替的效率,从而获得高执行率。

 

读写锁ReentrantReadWriteLock

  ReentrantReadWriteLock可以在多个线程同时进行时,允许一个写线程(不允许其他读线程和写线程)或者多个读线程的操作,支持读多写少的业务场景,提高程序性能。

读写锁模板

public class RwLockTemplate {

    private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    private Lock read = reentrantReadWriteLock.readLock();
    private Lock write = reentrantReadWriteLock.writeLock();
    
    public void set(Object params) {
        this.write.lock();
        try {
            // do my work
        } finally {
            this.write.unlock();
        }
    }
    
    public Object get() {
        this.read.lock();
        try {
            // do my work
            return null;
        } finally {
            this.read.unlock();
        }
    }
}

 

Condition接口

  既然已经有了Lock锁,那么他也会有一套等待通知机制,他就是Condition接口,对应synchronized的wait()、notify()、notifyAll()方法。

condition模板

public class ConditionTemplete {

    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    
    public void waitc() throws InterruptedException {
        lock.lock();
        try {
            condition.await();
        } finally {
            lock.unlock();
        }
    }
    
    public void notityc() throws InterruptedException {
        lock.lock();
        try {
            condition.signal();
            //condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
}

这里await()方法就是等待,signal()/signalAll()方法就是通知,condition可以有多个

注:这里的signal()/signalAll()方法和synchronized的notify()/notifyAll()方法有所不同,signal()通知这个lock的所有等待signalAll()通知程序中所有lock的所有等待

 

使用Lock和Condition实现阻塞队列

public class BlockingQueueLC<T> {

    private List<T> queue = new LinkedList<>();
    private int limit;
    private Lock lock = new ReentrantLock();
    private Condition needNotEmpty = lock.newCondition();
    private Condition needNotFull = lock.newCondition();
    
    public BlockingQueueLC(int limit) {
        this.limit = limit;
    }

    public void push(T el) throws InterruptedException {
        this.lock.lock();
        try {
            while (this.limit == this.queue.size()) {
                this.needNotFull.await();
            }
            this.queue.add(el);
            this.needNotEmpty.signal();
        } finally {
            this.lock.unlock();
        }
    }
    
    public T pull() throws InterruptedException {
        this.lock.lock();
        try {
            while (0 == this.queue.size()) {
                this.needNotEmpty.await();
            }
            this.needNotFull.signal();
            return this.queue.get(0);
        } finally {
            lock.unlock();
        }
    }
}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!