浅谈Lock
javaLock是一个接口:
根据方法名字,基本知道方法的主要用途。命名很重要。。。
ReentrantLock
,可重入锁,ReentrantLock是唯一实现了Lock接口的类,简单的测试:
Lock应该放在成员变量里面,如果放在方法里面,每次执行都会new一个新的Lock,就不是同一个lock了
lock放在方法里面,执行出来多个线程在获得锁:
tryLock() & tryLock(long time, TimeUnit unit)顾名思义,就是尝试获取锁,可以加时间,tryLock(long time, TimeUnit unit) 能够响应中断,即支持对获取锁的中断,但是但尝试获取一个内部锁(synchronized)的操作是不能被中断,返回的是boolean类型这里可以进行判断,如果获取到了锁,则进行啥,若没有获取到锁执行啥,
测试代码:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
private Lock lock = new ReentrantLock();
/**
* true 表示 ReentrantLock 的公平锁
*/
private ReentrantLock lock = new ReentrantLock(true);
public static void main(String[] args) {
//简单的使用
test1();
//trylock
test2();
//中断锁
test3();
}
public static void test1() {
LockTest lockTest = new LockTest();
for (int i = 0; i < 10; i++) {
//线程1
new Thread(() -> {
lockTest.method1(Thread.currentThread());
}, i + "").start();
}
}
//需要参与同步的方法
private void method1(Thread thread) {
//错误
// Lock lock = new ReentrantLock();
lock.lock();
try {
System.out.println("线程名" + thread.getName() + "获得了锁");
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("线程名" + thread.getName() + "释放了锁");
lock.unlock();
}
}
public static void test2() {
LockTest lockTest = new LockTest();
for (int i = 0; i < 10; i++) {
//线程1
new Thread(() -> {
lockTest.method2(Thread.currentThread());
}, i + "").start();
}
}
//需要参与同步的方法
private void method2(Thread thread) {
// 尝试获取锁
boolean b = lock.tryLock();
// 尝试获取锁时间
// boolean b = false;
// try {
// System.out.println("线程名" + thread.getName() + "尝试获取锁--");
// b = lock.tryLock(2, TimeUnit.SECONDS);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
if (!b) {
//未获取到锁
System.out.println("线程名" + thread.getName() + "没有获取到锁,直接返回--");
return;
}
//获取到锁
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("线程名" + thread.getName() + "获得了锁");
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("线程名" + thread.getName() + "释放了锁");
lock.unlock();
}
}
public static void test3() {
LockTest lockTest = new LockTest();
//线程1
Thread t1 = new Thread(() -> {
try {
lockTest.method3(Thread.currentThread());
} catch (InterruptedException e) {
System.out.println("线程名t1中断-----");
// e.printStackTrace();
}
}, "t1");
t1.start();
//线程1
Thread t2 = new Thread(() -> {
try {
lockTest.method3(Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t2");
t2.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程名t1执行中断");
t1.interrupt();
}
//需要参与同步的方法
private void method3(Thread thread) throws InterruptedException {
lock.lockInterruptibly();
//获取到锁
System.out.println("线程名" + thread.getName() + "获得了锁");
try {
TimeUnit.SECONDS.sleep(10);
} catch (Exception e) {
// e.printStackTrace();
} finally {
System.out.println("线程名" + thread.getName() + "释放了锁");
lock.unlock();
}
}
}
默认的ReentrantLock是不公平的锁,
公平锁,公平指的是,谁先尝试获取锁,谁就能获取到锁,不公平的话,就是有可能一个线程最先尝试获取锁,但是一直没有获取到锁,获取锁都是随机的.可以通过构造方法创建公平锁:
非公平锁性能高于公平锁性能。首先,在恢复一个被挂起的线程与该线程真正运行之间存在着严重的延迟。而且,非公平锁能更充分的利用cpu的时间片,尽量的减少cpu空闲的状态时间。
ReadWriteLock
读写锁,跟lock接口没啥关系主要实现有ReentrantReadWriteLock
demo:
public class ReadWriteLockTest {
private static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private static Integer count = 0;
public static void main(String[] args) {
ReadWriteLockTest readWriteLockTest = new ReadWriteLockTest();
for (int i = 0; i < 100; i++) {
//从1到10的int型随数
int j = (int) (1 + Math.random() * (10 - 1 + 1));
if (j % 4 != 1) {
//写操作
new Thread("" + i) {
public void run() {
readWriteLockTest.get(Thread.currentThread());
}
}.start();
} else {
//读操作
int co = i;
new Thread("" + i) {
public void run() {
readWriteLockTest.write(Thread.currentThread(), co);
}
}.start();
}
}
}
public static void get(Thread thread) {
rwl.readLock().lock();
try {
System.out.println("线程" + thread.getName() + "开始读操作...");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" + thread.getName() + "读操作完毕..." + count);
} finally {
rwl.readLock().unlock();
}
}
public static void write(Thread thread, int i) {
rwl.writeLock().lock();
try {
System.out.println("线程" + thread.getName() + "开始写操作---------"+i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
count = i;
System.out.println("线程" + thread.getName() + "开始写操作完成------"+i);
} finally {
rwl.writeLock().unlock();
}
}
}
执行结果:
开始读操作,线程可以都进行读操作,要是有写操作,会等读操作都完成.
Lock和synchronized的选择
总的来说,Lock和synchronized有以下几点不同:
(1) Lock是一个接口,是JDK层面的实现;而synchronized是Java中的关键字,是Java的内置特性,是JVM层面的实现;
(2) synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
(3) Lock 可以让等待锁的线程响应中断,而使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
(4) 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到;
(5) Lock可以提高多个线程进行读操作的效率。
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的。而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。