Java 锁机制

来源:互联网 发布:java库函数查询工具 编辑:程序博客网 时间:2024/06/11 18:29

Java中,锁分为内部锁与显式锁两种类型。

内部锁又分为对象锁与类锁。


Java中,每个类的java对象内部都有一个锁,称为对象锁;每个类都有一个唯一的class对象,class对象内部锁,称为类锁,主要用来同步静态方法与静态代码块。两者都可以直接在方法前使用synchronized修饰来获得,也可以在代码块上使用synchronize(this)来获取对象锁,使用synchronized(Class.class)来获得类锁。(Class.class为类名)

对象锁与对象的属性,方法是没有什么内在联系的,所以使用某个对象的内部锁时,完全可以对其进行其他操作。

因为类锁只有一个,所以所有类的对象实例共享。

JDK5.0之后,Java中加入了java.util.concurrent包,其中的子包locks里面提供了显式锁,ReentrantLock和ReentrantReadWriteLock。显式锁比起内部锁要更加灵活方便与多样,提供了定时和轮询的功能,此外还加入了读写锁,即ReentrantReadWriteLock。

ReentrantLock实现了lock接口

public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();}

对于内部锁,synchronized方法,如果获取不到锁,线程便会阻塞,一直等待。这与显式锁的lock()方法一致。

显示锁还提供了lockInterruptibly方法。当线程因为获取内部锁而阻塞时,对该线程的中断也会产生阻塞,当线程最终获得了锁之后,线程的阻塞才会生效。而lockInterruptibly方法便可以让线程在等待获取锁时,也能顾响应中断。


显示锁中的trylock方法可以对锁的占用情况进行询问,并立即返回结果。如果该锁当前空闲,便会返回true,并获取锁;若锁当前被占用,则返回false。同时,trylock方法还实现了定时功能,在指定时间内不断对锁的占用情况进行询问,返回结果同上。第一个参数为时长,第二个参数为时间单位。

在内部锁中,一旦程序执行结束,系统将自动解锁,但在显示锁中,需要调用unlock方法进行手动解锁。

此外,显示锁还提供了Condition来丰富lock的功能。每一个Condition都相当于一个阻塞队列。

内部锁中,使用wait(),notify()/notifyAll()方法对线程进行阻塞和唤醒来实现锁的控制,在显示锁中,使用condition的await(),signal()和signalAll()方法来实现对锁的控制。


显式锁实现的生产者-消费者线程:

public class Queue {    private int maxsize = 10;    private LinkedList<Integer> storage = new LinkedList<>();    volatile int count = 0;    private ReentrantLock reentrantLock = new ReentrantLock();    private Condition empty = reentrantLock.newCondition();    private Condition full = reentrantLock.newCondition();    Queue() {    }    public void put() {        reentrantLock.lock();        try {            while (storage.size() == maxsize) {                System.out.println(Thread.currentThread().getName() + ": wait\n");                full.await();            }            count ++;            System.out.println(Thread.currentThread().getName() + ": add " + count + "\n");            storage.add(count);            Thread.sleep(1000);            empty.signalAll();        } catch (InterruptedException e) {            e.printStackTrace();        }    }    public void take() {        reentrantLock.lock();        try {            while (storage.size() == 0) {                System.out.println(Thread.currentThread().getName() + ": wait\n");                empty.await();            }            System.out.println(Thread.currentThread().getName() + ": remove" + storage.getFirst() + "\n");            storage.removeFirst();            Thread.sleep(1000);            full.signalAll();        } catch (InterruptedException e) {            e.printStackTrace();        }    }}


内部锁实现的生产者消费者线程:

public class Queue {    private int maxsize = 10;    private LinkedList<Integer> storage = new LinkedList<>();    volatile int count = 0;    Queue() {    }    public synchronized void put() {        try {            while (storage.size() == maxsize) {                System.out.println(Thread.currentThread().getName() + ": wait\n");                wait();            }            count ++;            System.out.println(Thread.currentThread().getName() + ": add " + count + "\n");            storage.add(count);            Thread.sleep(1000);            notifyAll();        } catch (InterruptedException e) {            e.printStackTrace();        }    }    public synchronized void take() {        try {            while (storage.size() == 0) {                System.out.println(Thread.currentThread().getName() + ": wait\n");                wait();            }            System.out.println(Thread.currentThread().getName() + ": remove" + storage.getFirst() + "\n");            storage.removeFirst();            Thread.sleep(1000);            notifyAll();        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

java.util.concurrent包中还引入了读写锁,ReadWriteLock。ReadWriteLock分为ReadLock和WriteLock,当使用读锁锁住时,其他所有的读锁都可重入,而写锁不可入。当写锁锁住时,其他所有的锁,无论写锁还是读锁,都不可重入。如果没有写锁的情况下,读是没有阻塞的,这在一定程度上,提高了程序的效率。


0 0
原创粉丝点击