内容目录
使用Lock来代替synchronized
正文:
上一篇中我们讲到了,死锁问题以及解决死锁问题常见的一些解决方案,现在我们简单的总结一下,synchronized的一些特点
synchronized:
1、简单,不要手动释放锁,jvm会自动帮我们释放锁资源
2、synchronized同步块中的锁的使用
package test3;
public class T implements Runnable{
static Object aa = "1aa";
static int i = 10;
@Override
public void run() {
while(i > 0) {
synchronized(this) {
System.out.println("i="+i);
i--;
}
try {
Thread.sleep(1 * 1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
T t = new T();
Thread thread1 = new Thread(t,"1");
Thread thread2 = new Thread(t,"2");
thread1.start();
thread2.start();
}
}
此时我们同步块的锁的当前这个类本身,我们可以得出一个规律:
当我们用多个Thread对象的时候,锁是当前线程本身
当我们用一个Thread对象的时候则锁是一个类变量
不是很理解的可以把我们前一篇的实例来过来,做一个对比,然后好好揣摩一下
3、synchronized使用不灵活,拓展性不好,所以我们使用lock来替代synchronized
4、必须要有一把锁标识,即类变量或者this即线程本身
5、没有超时机制,即如果发生死锁问题,系统很难自动解决
为了解决锁的拓展性问题,我们下面介绍锁的另一种常用方法,即java.util.concurrent.locks包下面的lock接口
Lock接口的基本介绍:
1、Lock是一个接口,它用来提供比synchronized更加广泛的锁定操作
2、需要手动释放资源,并且有超时机制,可以有效避免死锁问题
3、可以获取Condition对象以执行线程的挂起操作
介绍:
public Interface Lock 中的函数包括
public void lock(); --> 获取锁
public void lockInterruptibly(); --> 如果线程没有中断则获取锁
public booleantryLock()/tryLock(long time , TimeUnit nano); --> 只有在调用的时候才获取锁/在指定的毫秒加上纳诺秒获取锁
public Condition newCondition(); --> 获取线程状态的对象
public void unLock(); --> 释放锁
Lock接口的实现类
ReentrantLock , ReentrantReadWriteLock.writeLock , ReentrantReadWriteLock.readLock
下面我们就ReentrantLock的使用做一个简单的介绍
package test3;
import java.util.concurrent.locks.ReentrantLock;
public class T {
public static void main(String args[]) {
T1 t1 = new T1();
T2 t2 = new T2("t1" , t1);
T2 t3 = new T2("t2" , t1);
t2.start();
t3.start();
}
}
class T1 {
private final ReentrantLock lock = new ReentrantLock();
void test(){
try {
lock.lockInterruptibly();
System.out.println(lock.isLocked());
System.out.println(Thread.currentThread().getName());
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
class T2 extends Thread{
T1 t1 ;
T2(String name , T1 t1){
super(name);
this.t1 = t1;
}
@Override
public void run() {
t1.test();
}
}
结果:
true
t1
true
t2
分析:
我们可以看出t1和t2是同步的,即等到t1资源释放以后t2才运行
ReentrantLock还有一些常用的函数,以此来帮我们更好的判断当前锁的使用是否合理
public int getHoldCount(); 当前线程保持此锁的次数
public int getQueueLength(); 查看后面有多少线程在等待
public boolean hasQueueThread()/hasQueueThread(Thread thread); 查看是否还有线程等待获取此锁
public boolean isHeldByCurrentThread(); 查看当前线程是否获得此锁
通过上面的函数可以非常灵活的知道我们当前锁的使用情况