@[toc]
Lock锁
在 jdk1.5 之后,并发包中新增了 Lock 接口(以及相关实现类)用来实现锁功能,Lock 接口提供了与 synchronized 关键字类似的同步功能,但需要在使用时手动获取锁和释放锁,且在使用上比synchronized更加灵活。
1、使用ReentrantLock实现同步
案例:
package com.yxl.demo.ThreadTest;
public class test5 {
public static void main(String[] args) {
TestDemo thread = new TestDemo();
Thread t1 = new Thread(thread,"窗口一");
Thread t2 = new Thread(thread,"窗口二");
t1.start();
t2.start();
}
}
class TestDemo implements Runnable{
//共享的火车票变量
private int count = 100;
//重寫run方法
@Override
public void run() {
while (count > 0){
try {
//休眠一下 方便出现并发问题
Thread.sleep(50);
}catch (Exception e){
e.getMessage();
}
sale();
}
}
//卖票
public void sale(){
if(count > 0){
System.out.println(Thread.currentThread().getName() +"出售 :" +(100 - count + 1));
count--;
}
}
}
运行结果如下:会发现出现卖重复票的问题
除了 内置锁(Synchronized)解决方案,我们可以使用 Lock锁解决
synchronized的缺陷
synchronized是Java中的一个关键字。
我们知道如果一段代码被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块的时候,其他的线程便只能一直等待,等待获取锁的线程释放锁,而这里获锁的线程释放锁有两种情况:
- 获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
- 线程执行发生异常,此时JVM会让线程自动释放锁。
如果这个获取锁的线程由于I/O或者其他原因(如调用sleep方法)被阻塞了,但是有没有释放锁,其他线程便只能干巴巴地等待,这会很影响执行效率。
因此就需要一种机制可以不让等待的线程一直无限期地等待下去(如只等待一定的时间或者能够响应中断),通过Lock就可以办到。
再如:当有多个线程读取文件时,读操作和写操作会发生冲突,写操作和写操作会发生冲突,但是读操作和读操作不会发生冲突。
如果此时采用synchronized关键字就会出现一个问题:如果多个线程只是进行读操作,所以当一个线程进行读操作时,其他的线程只能等待无法进行读操作。
因此就需要 一种机制使得多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock就可以办到。
另外,通过Lock可以知道线程有没有成功获取锁,这是synchronized无法办到的。
注意:
- Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性,Lock是一个类(接口),通过这个类可以实现同步访问;
- 使用synchronized不需要用户手动去释放锁,当synchronized方法或代码块执行完后,系统会自动让线程释放对锁的占用;而Lock则必须要用户手动释放锁,如果没有主动释放,就有可能导致出现死锁。
使用Lock锁案例
package com.yxl.demo.ThreadTest;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class test5 {
public static void main(String[] args) throws InterruptedException {
TestDemo thread = new TestDemo();
Thread t1 = new Thread(thread,"窗口一");
Thread t2 = new Thread(thread,"窗口二");
t1.start();
t2.start();
}
}
class TestDemo implements Runnable{
//共享的火车票变量
private int count = 100;
//重寫run方法
@Override
public void run() {
while (count > 0) {
sale();
}
}
public void sale() {
Lock lock = new ReentrantLock();
lock.lock();
try {
if (count > 0) {
System.out.println(Thread.currentThread().getName() + "出售 :" + (100 - count + 1));
count--;
}
}catch (Exception e){
}finally {
//一定在finally中释放锁
lock.unlock();
}
}
}
lock.lock() 获取锁
lock.unlock() 释放锁,但要在 finally中使用lock.trylock() 获取锁 ,也要结合 try catch 一起使用