ReentranLock重入锁类, 实现自Lock接口
推荐ReentrantLock,使用相对效率比synchronize高,因为量级较轻
使用重入锁,必须必须必须手工释放锁标记。一般都是在finally代码块中定义释放锁标记的unlock方法。
上代码
lock到unlock,相当于就是synchronize方法的开始到结束
public class Test_01 {
Lock lock = new ReentrantLock();
void m1(){
try{
lock.lock(); // 相当于synchronize给对象加锁,这行以下一直到 lock.unlock()就被加锁了
for(int i = 0; i < 10; i++){
TimeUnit.SECONDS.sleep(1);
System.out.println("m1() method " + i);
}
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock(); // 解锁
}
}
void m2(){
lock.lock();
System.out.println("m2() method");
lock.unlock();
}
public static void main(String[] args) {
final Test_01 t = new Test_01();
new Thread(new Runnable() {
@Override
public void run() {
t.m1();
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
t.m2();
}
}).start();
}
}
尝试锁:
1、isLocked = lock.tryLock();
尝试锁, 如果有锁,无法获取锁标记(即加锁),返回false。
如果获取锁标记,返回true(加锁成功)
2、isLocked = lock.tryLock(5, TimeUnit.SECONDS);
阻塞尝试锁,阻塞参数代表的时长,在这个时长里尝试获取锁标记,成功,返回true。
如果超时,不等待。直接返回false(加锁失败)。
上代码
public class Test_02 {
Lock lock = new ReentrantLock();
void m1(){
try{
lock.lock();
for(int i = 0; i < 10; i++){
TimeUnit.SECONDS.sleep(1);
System.out.println("m1() method " + i);
}
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
void m2(){
boolean isLocked = false;
try{
// isLocked = lock.tryLock();
// 阻塞尝试锁,阻塞参数代表的时长,在这个时长里尝试获取锁标记,成功,返回true。
// 如果超时,不等待。直接返回false(加锁失败)。
isLocked = lock.tryLock(5, TimeUnit.SECONDS);
if(isLocked){
System.out.println("m2() method synchronized");
}else{
System.out.println("m2() method unsynchronized");
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(isLocked){
// 尝试锁在解除锁标记的时候,一定要判断是否获取到锁标记。
// 如果当前线程没有获取到锁标记,执行unlock会抛出异常。
lock.unlock();
}
}
}
public static void main(String[] args) {
final Test_02 t = new Test_02();
new Thread(new Runnable() {
@Override
public void run() {
t.m1();
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
t.m2();
}
}).start();
}
}
打断锁:
只能打断阻塞状态的线程,无法打断执行状态的线程
* 阻塞状态: 包括普通阻塞,等待队列,锁池队列。
* 普通阻塞: sleep(10000), 可以被打断。调用thread.interrupt()方法,可以打断阻塞状态,抛出异常。
* 等待队列: wait()方法被调用,也是一种阻塞状态,只能由notify唤醒。无法打断
* 锁池队列: 无法获取锁标记。不是所有的锁池队列都可被打断。
* 使用ReentrantLock的lock方法,获取锁标记的时候,如果需要阻塞等待锁标记,无法被打断。
* 使用ReentrantLock的lockInterruptibly方法,获取锁标记的时候,如果需要阻塞等待,可以被打断。
上代码
public class Test_03 {
Lock lock = new ReentrantLock();
void m1(){
try{
lock.lock(); //加锁
for(int i = 0; i < 5; i++){
TimeUnit.SECONDS.sleep(1);
System.out.println("m1() method " + i);
}
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock(); //解锁
}
}
void m2(){
try{
lock.lockInterruptibly(); // 阻塞等待锁。与lock的区别在于可以被其他的线程打断阻塞状态
System.out.println("m2() method");
}catch(InterruptedException e){
System.out.println("m2() method interrupted");
}finally{
try{
lock.unlock(); //解锁
}catch(Exception e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
final Test_03 t = new Test_03();
new Thread(new Runnable() {
@Override
public void run() {
t.m1();
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
t.m2();
}
});
t2.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.interrupt(); // 打断线程休眠。非正常结束阻塞状态的线程,都会抛出异常。
}
}
公平锁
多个阻塞的线程想要获取锁,那么谁能抢到锁,获得执行时间呢?
非公平锁:多个线程竞争,谁抢到谁加锁,synchronize是公平锁。
公平锁效果:轮询的执行多个线程,不适合在线程数过多时使用(因为要不断的记录每个线程等待时间)
如下图所示,公平锁会记录每个线程的等待时间,当锁释放后,优先执行阻塞时间长的线程。
上代码
public class Test_04 {
public static void main(String[] args) {
TestReentrantlock t = new TestReentrantlock();
//TestSync t = new TestSync();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
t2.start();
}
}
class TestReentrantlock extends Thread{
// 定义一个公平锁
private static ReentrantLock lock = new ReentrantLock(true);
public void run(){
for(int i = 0; i < 5; i++){
lock.lock();
try{
System.out.println(Thread.currentThread().getName() + " get lock");
}finally{
lock.unlock();
}
}
}
}
class TestSync extends Thread{
public void run(){
for(int i = 0; i < 5; i++){
synchronized (this) {
System.out.println(Thread.currentThread().getName() + " get lock in TestSync");
}
}
}
}
重入锁&条件
* 条件 - Condition, 为Lock增加条件。当条件满足时,做什么事情,如加锁或解锁。如等待或唤醒
这里使用一个生产者消费者案例来说明。
自定义同步容器,容器容量上限为10。可以在多线程中应用,并保证数据线程安全。
public class Test_00 {
public static void main(String[] args) {
final Container<Integer> container = new Container<Integer>();
//启动10个消费者线程
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 5; j++) {
System.out.println(container.get());
}
}
}, "consumer_Thread" + i).start();
}
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//启动10个消费者线程
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 5; j++) {
container.put(j);
}
}
}, "consumer_Thread" + i).start();
}
}
}
/**
* 自定义的容器
* @param <E>
*/
class Container<E>{
private final LinkedList<E> list = new LinkedList<E>();
private final int MAX = 10;
private int count = 0;
private Lock lock = new ReentrantLock();
private Condition producer = lock.newCondition(); //生产者条件锁
private Condition consumer = lock.newCondition(); //消费者条件锁
public int get_count(){
return count;
}
//生产者
public void put(E num){
lock.lock();
try {
while(list.size() == MAX){
//容量满了,阻塞生产者
System.out.println(Thread.currentThread().getName() + "等待");
producer.await();
}
//容量不满,加入元素,唤醒所有消费者
list.add(num);
count++;
consumer.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock(); //最后无论如何一定要解锁
}
}
public E get(){
lock.lock();
E poll = null;
try {
while(get_count() == 0){
//容量空了,阻塞消费者
System.out.println(Thread.currentThread().getName() + "等待");
consumer.await();
}
//容量不空,唤醒消费者
poll = list.poll();
count--;
producer.signalAll();
} catch (Exception e) {
e.printStackTrace();
}finally{
lock.unlock();
}
return poll;
}
}
以上就是关于ReentrantLock,我的一些认识和理解,希望对你有所帮助,欢迎留言、交流、点赞、关注。谢谢!