当前位置: 首页 > Java > 正文

synchronized,Lock,Condition

1 星2 星3 星4 星5 星 (1 次投票, 评分: 5.00, 总分: 5)
Loading ... Loading ...
baidu_share

谈到Java中的锁,大部分时候自然而然想到的还是内置锁,即synchronized方法或synchronized块;自jdk1.5开始,Java提供了Lock接口。那么Lock是否是内置锁的替代品,它们之间的区别和联系又有哪些呢?

内置锁与Lock之间的相同点
1、相同的内存语义。两者都可以保证内存可见性,都可以实现互斥访问。
2、synchronized是可重入的,Lock也可以实现成可重入的,且绝大部分实现都是可重入的。所谓重入,就是一个线程获取一个锁后,还可以继续获取该锁。如两个使用了相同锁的实例方法间的调用。

它们间的不同点
先看看Lock接口中与锁相关的方法声明:

void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();

使用Lock,典型的代码结构如下:

Lock lock = ...
lock.lock();
try {
    //...do sth.
} finally {
    lock.unlock();
}

锁定(包括lock、lockInterruptibly,两个重载的tryLock)与unlock总是成对出现的。目前类库中与Lock接口相关的实现有ReentrantLock,ReentrantReadWriteLock等(ReentrantReadWriteLock实现的是ReadWriteLock接口,其内部使用的ReadLock和WriteLock类实现了Lock接口)。那么Lock与内置锁的区别究竟有哪些呢?

1、内置锁是基于花括号的,进入同步块/方法(为方便,后文合称两者为同步区域)即获取到锁,退出同步区域会自动释放锁,无论是正常退出还是抛出异常,jvm都会自动释放锁;而Lock则需要手动的释放锁,且一般放到finally中,否则抛出异常可能导致锁永远得不到释放的情形。

2、如果锁竞争很严重,当代码执行到进入同步区域之前,即获取锁之前,会一直等待,直到成功获取锁,但在这个过程中,无法取消此操作(无法中断)。而Lock中的lockInterruptibly则能响应中断请求。

3、Lock中的tryLock方法可配合使用轮询,可避免错序死锁的发生。

4、内置锁在退出同步区域就释放了锁,而Lock可以在一个方法里获取,在另一个地方unlock。无论这种做法是好是坏,它增加了Lock的灵活性。

5、在构造ReentrantLock和ReentrantReadWriteLock的时候可以指定一个公平策略。如果是公平的,即构造的时候传入的参数为true,那么每次获取锁的时候都会加入到等待锁的队列,按顺序获取锁。如果是非公平的,即传入的参数为false,那么即使队列中有很多线程在等待锁,如果当前一个正在运行的线程需要锁,碰巧发现锁当前正被释放,那么它就可以直接获得该锁,而无需排队。这样做的一个好处就是减少了上下文切换。当锁竞争严重时,上下文切换带来的系统开销会非常大。内置锁,无法指定采用公平策略或非公平策略,且也没有规定内部锁该采用何种策略,大部分jvm实现中,内置锁都是非公平的。

6、在jdk1.5的时候,内置锁与Lock之间的性能差距比较大,内置锁的效率较低;但在jdk1.6的时候,内置锁采用了类似于Lock的实现机制,它们之间的性能差异已经不大了。

7、读写锁允许多个线程访问被保护的内容,只在出现写-写,读-写的时候才需要互斥访问,当读操作比较多的时候,使用读写锁能大大提升性能。而内置锁不好做这方面的控制。

Lock带来比内置锁多得多的特性,可供程序操作。但这并不是说内置锁就不再有用了,使用内置锁有着代码结构简单,自动释放锁等便利优点,如果不需要Lock独有而内置锁中缺乏的特性(包括功能、性能等)时,还是使用内置锁更合适一些。

Lock处理线程通信通过Condition来实现。Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

在Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现,这里注意,Condition是被绑定到Lock上的,要创建一个Lock的Condition必须用newCondition()方法。

Condition的强大之处在于它可以为多个线程间建立不同的Condition。例如ArrayBlockingQueue类中

    /** Main lock guarding all access */
    private final ReentrantLock lock;
    /** Condition for waiting takes */
    private final Condition notEmpty;
    /** Condition for waiting puts */
    private final Condition notFull;
 
    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = (E[]) new Object[capacity];
        //建立一个非公平锁
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }
 
    public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        final E[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            try {
                //数组满了,等待
                while (count == items.length)
                    notFull.await();
            } catch (InterruptedException ie) {
                notFull.signal(); // propagate to non-interrupted thread
                throw ie;
            }
            insert(e);
        } finally {
            lock.unlock();
        }
    }
 
    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            try {
                //数组没有元素,等待。
                while (count == 0)
                    notEmpty.await();
            } catch (InterruptedException ie) {
                notEmpty.signal(); // propagate to non-interrupted thread
                throw ie;
            }
            E x = extract();
            return x;
        } finally {
            lock.unlock();
        }
    }

从以上例子看出,同一个锁,可以有多个条件,从而满足不同的应用场景。notEmpty用来判定数组是否为空。notFull用来判定数组是否超出其长度。

现在用一个Lock,Condition来实现一个wait—–notify的例子。
message实体:

public class Message {
	private String text;
 
	public Message(String text) {
		this.text = text;
	}
 
	public String getText() {
		return text;
	}
 
	public void setText(String text) {
		this.text = text;
	}
}

waiter线程

public class Waiter implements Runnable{
 
	Message message;
	private Lock lock;
	Condition condition;
 
	public Waiter(Message message,Lock lock,Condition condition) {
		this.message = message;
		this.lock=lock;
		this.condition=condition;
	}
 
	@Override
	public void run() {	
			lock.lock();
			try {
				System.out.println("Waiter is waiting for the notifier at " + new Date());
				//Condition condition=lock.newCondition();
				condition.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}finally{
				lock.unlock();
			}
 
		System.out.println("Waiter is done waiting at " + new Date());
		System.out.println("Waiter got the message: " + message.getText());		
	}
 
}

Notifier线程

public class Notifier implements Runnable{
	Message message;
	private Lock lock;
	Condition condition;
 
	public Notifier(Message message,Lock lock,Condition condition) {
		this.message = message;
		this.lock=lock;
		this.condition=condition;
	}
 
	@Override
	public void run() {
		System.out.println("Notifier is sleeping for 3 seconds at " + new Date());
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		lock.lock();
		try{
			message.setText("Notifier took a nap for 3 seconds");
			System.out.println("Notifier is notifying waiting thread to wake up at " + new Date());
			//Condition condition=lock.newCondition();
			condition.signalAll();
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
 
	}
}

测试主函数:

public class WaitNotifyExample {
	public static void main(String[] args) {
		Lock lock = new ReentrantLock();
		Condition condition = lock.newCondition();
		Message message = new Message("Howdy");
 
		Waiter waiter = new Waiter(message,lock,condition);
		Thread waiterThread = new Thread(waiter, "waiterThread");
		waiterThread.start();
 
		Notifier notifier = new Notifier(message,lock,condition);
		Thread notifierThread = new Thread(notifier, "notifierThread");
		notifierThread.start();
 
	}
}

本文固定链接: http://www.chepoo.com/synchronized-lock-condition.html | IT技术精华网

synchronized,Lock,Condition:等您坐沙发呢!

发表评论