Lock锁的使用模板及API解释

    实现同步,我们可以使用 synchronized ,也可以使用 Java SE 5 之后新增的 Lock 接口。

synchronized 和 Lock 的区别

1)synchronized 是 Java 中的关键字;Lock 是个接口
2)synchronized 在发生异常时,会自动释放锁;Lock 必须手动释放锁,应该使用 try…finally ,避免死锁
3)Lock 等待锁的过程中可以使用 interrupt 中断等待;synchronized 不能响应中断
4)Lock 可以判断当前线程是否获取到锁;synchronized 不能
5)Lock 有多个子类,实现了多样化的锁

Lock API

方法名称描述模板
void lock()获取锁lock.lock();try {} finally {lock.unlock();}
void lockInterruptibly() throws InterruptedException调用lockInterruptibly获取锁的线程,在等待锁和在获取锁但还没获取到锁的一瞬间可以被其他线程中断。如果没有被其他线程中断,并且锁是空闲的,则成功获取到锁,获取到锁之后,即使其他线程调用了该线程的中断,也不会使线程中断try { lock.lockInterruptibly();try{}finally{lock.unlock();}} catch (InterruptedException e) {e.printStackTrace();}
boolean tryLock()返回 true表示已获取到锁,返回 false表示不能获取到锁,此方法不会阻塞执行线程if (lock.tryLock()){try {} finally {lock.unlock();}}else {}
boolean tyrLock(long time,TimeUnit unit) throws InterruptedException在超时时间内尝试获取锁,返回true表示已获取到锁,返回false表示不能获取到锁同tryLock()
void unlock释放锁没有获取锁而直接调用unlock会出现java.lang.IllegalMonitorStateException异常,针对上面获取锁的方式,一定要注意unlock的位置
Condition newCondition获取等待通知组件,该组件与当前线程绑定,当前线程只有获得了锁,才能调用该组件的 await()方法和 signalAll()方法,功能与任意一个Java对象的wait()、wait(long timeout)、notify() 以及 notifyAll() 方法一致

阿里巴巴开发手册总结

在使用阻塞等待获取锁的方式中,必须在 try 代码块之外,并且在加锁方法与 try 代码块之间没有任何可能抛出异常的方法调用,避免加锁成功后,在 finally 中无法解锁。
说明一:如果在 lock 方法与 try 代码块之间的方法调用抛出异常,那么无法解锁,造成其它线程无法成功获取锁。
说明二:如果 lock 方法在 try 代码块之内,可能由于其它方法抛出异常,导致在 finally 代码块中,unlock对未加锁的对象解锁,它会调用 AQS 的 tryRelease 方法(取决于具体实现类),抛出IllegalMonitorStateException 异常。
说明三:在 Lock 对象的 lock 方法实现中可能抛出 unchecked 异常,产生的后果与说明二相同。
正例:

Lock lock = new XxxLock();
// ...
lock.lock();
try {
	doSomething();
	doOthers();
} finally {
	lock.unlock();
}

反例:

Lock lock = new XxxLock();
try {
	// 如果此处抛出异常,则直接执行 finally 代码块
	doSomething();
	// 无论加锁是否成功,finally 代码块都会执行
	lock.lock();
	doOthers();
} finally {
	lock.unlock();
}

在使用尝试机制来获取锁的方式中,进入业务代码块之前,必须先判断当前线程是否持有锁。锁的释放规则与锁的阻塞等待方式相同。
说明:Lock 对象的 unlock 方法在执行时,它会调用 AQS 的 tryRelease 方法(取决于具体实现类),如果当前线程不持有锁,则抛出 IllegalMonitorStateException 异常。
正例:

Lock lock = new XxxLock();
// ...
boolean isLocked = lock.tryLock();
if (isLocked) {
	try {
		doSomething();
		doOthers();
	} finally {
		lock.unlock();
	}
}

重入锁

    重入锁 ReentrantLock,它表示该锁能够支持一个线程对资源的重复加锁。该锁还支持获取锁时的公平和非公平性选择。
synchronized 关键字隐式的支持重进入,比如一个 synchronized 修饰的递归方法,在方法执行时,执行线程在获取了锁之后仍嫩连续多次地获得该锁。
ReentrantLock 不是隐式的重进入,而是在调用 lock() 方法时,已经获取到锁的线程,能够再次调用 lock() 方法获取锁而不被阻塞。
    公平和非公平选择是指,先对锁进行获取的请求一定先被满足,那么这个锁是公平的,反之,是不公平的。也可以说锁获取是有顺序的。ReentrantLock 提供了一个构造函数,能够控制锁是否是公平的。
    事实上,公平的锁机制一般情况下没有非公平的效率高,但是公平锁能够保证等待越久的请求优先得到满足。

读写锁

    ReentrantLock 是排他锁,这表示锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读和一个写,通过分离读锁和写锁提升性能。
    ReadWriteLock 接口仅定义了获取读锁和写锁的两个方法,readLock() 和 writeLock() 。ReentrantReadWriteLock 实现了 ReadWriteLock 的方法,并且提供了便于外界监控其内部工作状态的方法。

Condition 接口

    任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object上),主要包 wait()、wait(long timeout)、notify() 以及 notifyAll() 方法,这些方法与 synchronized 同步关键字配合,可以实现等待/通知模式。Condition 接口也提供了类似 Object 的监视器方法,与 Lock 配合可以实现等待/通知模式,但是这两者在使用方式以及功能特性上还是有差别的。

基于 Lock 实现的生产者消费者 Demo

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ProducerConsumer {
	public static void main(String[] args) {
		Goods goods = new Goods();
		new Thread(new Producer(goods), "生产者---01---").start();
		new Thread(new Producer(goods), "生产者---02---").start();
		new Thread(new Consumer(goods), "---消费者---01---").start();
		new Thread(new Consumer(goods), "---消费者---02---").start();
	}
	static class Consumer implements Runnable {
		private Goods goods;
		public Consumer(Goods goods) {
			this.goods = goods;
		}
		public void run() {
			while (true) {
				try {
					goods.get();
				} catch (InterruptedException e) {
				}
			}
		}
	}
	static class Producer implements Runnable {
		private Goods goods;
		private Producer(Goods goods) {
			this.goods = goods;
		}
		public void run() {
			while (true) {
				try {
					goods.set("商品");
				} catch (InterruptedException e) {
				}
			}
		}
	}
	static class Goods {
		private String name;
		private int count = 1;
		// true:生产的商品还未被消费;false:商品缺货
		private boolean flag = false;
		private Lock lock = new ReentrantLock();
		private Condition condition = lock.newCondition();
		public void set(String name) throws InterruptedException {
			lock.lock();
			// 多个生产者,一定要用 while 循环判断,否则会死锁
			try {
				while (!this.flag) {
					condition.await();
				}
				this.name = name + count++;
				this.flag = true;
				System.out.println(Thread.currentThread().getName() + this.name);
				condition.signalAll();
			}finally {
				lock.unlock();
			}
		}
		public void get() throws InterruptedException {
			lock.lock();
			// 多个消费者,一定要用 while 循环判断,否则会死锁
			try {
				while (!this.flag) {
					condition.await();
				}
				this.flag = false;
				System.out.println(Thread.currentThread().getName() + this.name);
				condition.signalAll();
			}finally {
				lock.unlock();
			}
		}
	}
}

    下面代码是使用两个 Condition,分别控制生产者和消费者的等待唤醒

static class Goods {
        private String name;
        private int count = 1;
        // true:生产的商品还未被消费;false:商品缺货
        private boolean flag = false;
        private Lock lock = new ReentrantLock();
        // 生产者
        private Condition condition_pro = lock.newCondition();
        // 消费者
		private Condition condition_con = lock.newCondition();
        public void set(String name) throws InterruptedException {
            lock.lock();
            // 多个生产者,一定要用 while 循环判断,否则会死锁
            try {
                while (!this.flag) {
	                // 生产者等待
                    condition_pro.await();
                }
                this.name = name + count++;
                this.flag = true;
                System.out.println(Thread.currentThread().getName() + this.name);
                // 消费者唤醒
                condition_con.signal();
            }finally {
                lock.unlock();
            }
        }
        public void get() throws InterruptedException {
            lock.lock();
            // 多个消费者,一定要用 while 循环判断,否则会死锁
            try {
                while (!this.flag) {
	                // 消费者等待
                    condition_con.await();
                }
                this.flag = false;
                System.out.println(Thread.currentThread().getName() + this.name);
                // 消费者唤醒
                condition_pro.signal();
            }finally {
                lock.unlock();
            }
        }
    }

基于读写锁的 Demo

public class Cache {
	static Map<String, Object> map = new HashMap<String, Object>();
	static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
	static Lock r = rwl.readLock();
	static Lock w = rwl.writeLock();

	// 获取一个key对应的value
	public static final Object get(String key) {
		r.lock();
		try {
			return map.get(key);
		} finally {
			r.unlock();
		}
	}
	// 设置key对应的value,并返回旧的value
	public static final Object put(String key, Object value) {
		w.lock();
		try {
			return map.put(key, value);
		} finally {
			w.unlock();
		}
	}
	// 清空所有的内容
	public static final void clear() {
		w.lock();
		try {
			map.clear();
		} finally {
			w.unlock();
		}
	}
}
  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WakeLock API 是 Web API 的一部分,它允许 Web 应用程序请求设备保持唤醒状态,从而防止设备进入休眠状态。WakeLock API 可以用于很多场景,比如音乐播放器、视频播放器、导航应用、游戏等需要保持设备唤醒的应用程序。 以下是使用 WakeLock API 的一些基本步骤: 1. 检查浏览器是否支持 WakeLock API: ```javascript if ('wakeLock' in navigator) { // 支持 WakeLock API } else { // 不支持 WakeLock API } ``` 2. 请求 WakeLock: ```javascript navigator.wakeLock.request('screen') .then(function(wakeLock) { console.log('WakeLock 已经被获取'); // WakeLock 对象可以用于解除 WakeLock }) .catch(function(error) { console.error('获取 WakeLock 失败:', error); }); ``` 上面代码中,`request()` 方法返回一个 Promise 对象,如果获取 WakeLock 成功,Promise 对象的 `then()` 方法会被调用,传入一个 WakeLock 对象;如果获取 WakeLock 失败,Promise 对象的 `catch()` 方法会被调用,传入一个 Error 对象。 3. 解除 WakeLock: ```javascript wakeLock.release() .then(function() { console.log('WakeLock 已经被解除'); }) .catch(function(error) { console.error('解除 WakeLock 失败:', error); }); ``` 上面代码中,`release()` 方法返回一个 Promise 对象,如果解除 WakeLock 成功,Promise 对象的 `then()` 方法会被调用;如果解除 WakeLock 失败,Promise 对象的 `catch()` 方法会被调用,传入一个 Error 对象。 需要注意的是,WakeLock API 会消耗设备电量,应该在不需要保持唤醒状态时尽快解除 WakeLock。此外,不同类型的 WakeLock 有不同的限制和行为,需要根据具体情况选择合适的 WakeLock 类型。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值