在 jdk1.5 之后,并发包中新增了 Lock 接口(以及相关实现类)用来实现锁功能,Lock 接口提供了与 synchronized 关键字类似的同步功能,但需要在使用时手动获取锁和释放锁。
Lock 接口可以尝试非阻塞地获取锁 当前线程尝试获取锁。如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁。
Lock 接口能被中断地获取锁 与 synchronized 不同,获取到锁的线程能够响应中断,当获取到的锁的线程被中断时,中断异常将会被抛出,同时锁会被释放。
Lock 接口在指定的截止时间之前获取锁,如果截止时间到了依旧无法获取锁,则返回。
在Java多线程中,可以使用synchronized关键字实现线程之间的同步互斥,在jdk1.5后新增的ReentrantLock类同样可达到此效果,且在使用上比synchronized更加灵活。
创建成员属性 private Lock lock = new ReentrantLock();
在需要锁的代码前加上lock.lock();
进行上锁
在需要锁的代码结束位置lock.unlock();
释放锁
使用lock的使用 尽量使用try catch finally
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyThread implements Runnable {
public static Integer num=1000;
private Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();//上锁
if (num<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"___"+num);
num--;
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//释放锁
}
}
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
for (int i = 0; i < 3; i++) {
new Thread(myThread ).start();
}
}
注意:
使用lock在发生异常时,如果没有主动通过unlock()去释放锁,则很可能造成死锁现象
synchronized关键字结合wait()和notify()及notifyAll()方法的使用可以实现线程的等待与通知模式。在使用notify()、notifyAll()方法进行通知时,被通知的线程是JVM随机选择的。
ReentrantLock类同样可以实现该功能,需要借助Condition对象,可实现“选择性通知”。Condition类是jdk1.5提供的,且在一个Lock对象中可以创建多个Condition(对象监视器)实例。
注意
await() 和 signal() 必须在lock.lock(); 加锁后才能使用
和wait()和notify() 必须在synchronized使用原理一样
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyCondition implements Runnable{
private Lock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void run() {
try {
//上锁
lock.lock();
System.out.println(" 开始等待时间:"+System.currentTimeMillis());
System.out.println("我陷入了等待...");
//线程等待
condition.await();
//释放锁
lock.unlock();
System.out.println("锁释放了!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//通知方法
public void signal(){
try {
上锁
lock.lock();
System.out.println("结束等待时间:"+System.currentTimeMillis());
//通知等待线程
condition.signal();
} finally {
lock.unlock();//释放锁
}
}
}
public class MyLock{
public static void main(String[] args) throws InterruptedException {
MyCondition myCondition = new MyCondition();
Thread thread1 = new Thread(myCondition,"线程1");
thread1.start();
Thread.sleep(3000);
myCondition.signal();
}
}
Object类中的wait(long timeout)方法等同于Condition类中的await(long time,TimeUnit unit)方法。
Object类中的notify()方法等同于Condition类中的singal()方法。
Object类中的notifyAll()方法等同于Condition类中的singalAll()方法。
注意 同一个 Condition对象才能唤醒等待的线程 这样的好处就是可以控制到底唤醒那个线程了
比如上面代码 condition.await(); 那么只能使用 condition.signal(); 进行唤醒 而不是condition1 condition2
而不像Object类中wait,和notify()… 随机进行唤醒正在等待状态的线程线程 如果有多个线程都在等待状态那么不好进行控制
锁Lock分为“公平锁”和“非公平锁”。
公平锁:表示线程获取锁的顺序是按照线程加锁的顺序来的进行分配的,即先来先得FIFO先进先出顺序。 (true) 不会出现一个线程执行多次的结果
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyThread implements Runnable {
public static Integer num=1000;
private ReentrantLock lock;
public MyThread(boolean isFair) {
lock = new ReentrantLock(isFair);
}
@Override
public void run() {
while (true){
try {
lock.lock();//上锁
if (num<=0){
break;
}
System.out.println(Thread.currentThread().getName()+"___"+num);
num--;
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//释放锁
}
}
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread(true);
for (int i = 0; i < 3; i++) {
new Thread((Runnable) myThread).start();
}
}
Thread-1___1000
Thread-2___999
Thread-0___998
Thread-1___997
Thread-2___996
Thread-0___995
Thread-1___994
Thread-2___993
Thread-0___992
非公平锁(默认):一种获取锁的抢占机制,是随机拿到锁的,和公平锁不一样的是先来的不一定先拿到锁,这个方式可能造成某些线程一直拿不到锁,结果就是不公平的·。 (false)
public static void main(String[] args) {
//默认或者设置当前为false
final MyService myService = new MyService(false);
private ReentrantLock lock;
public MyService(boolean isFair) {
lock = new ReentrantLock(isFair);
}
类似于这种
Thread-0___1000
Thread-0___999
Thread-0___998
Thread-0___997
Thread-0___996
Thread-0___995
Thread-0___994
其他线程有可能一直拿不到锁
主要的作用我们会有一种需求,在对数据进行读写的时候,为了保证数据的一致性和完整性
需要读和写是互斥的,写和写是互斥的,但是读和读是不需要互斥的,这样读和读不互斥性能更高些
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
支持公平锁
读操作相关的锁,也成为共享锁。 和没锁一样
lock.readLock().lock();
读锁
lock.readLock().unlock();
释放读锁
同一时间允许多个线程执行lock()后的代码
写操作相关的锁,也叫排他锁。 和同步一个原理
lock.writeLock().lock();
写锁
lock.writeLock().unlock();
释放写锁
同一时间只允许一个线程执行lock()后的代码。
未使用读写锁
Thread-2 have read data: 1000
Thread-3 have read data: 1000
Thread-6 have read data: 999
Thread-1 have read data: 1000
Thread-0 have read data: 1000
Thread-5 have read data: 995
Thread-8 have read data: 995
Thread-4 have read data: 995
Thread-10 have read data: 993
Thread-7 have read data: 994
Thread-9 have read data: 991
Thread-11 have read data: 992
读取了修改前的数据了 因为我在写的过程中还能被其他线程读取到数据
解决办法使用了读写锁 让读写分离
public class test {
private static Integer data = 1000; //共享数据,只能有一个线程能写该数据,但可以有多个线程同时读
static ReadWriteLock lock = new ReentrantReadWriteLock();
public static void main(String[] args) {
for(int i=0;i<1000;i++){
new Thread(new Runnable() {
@Override
public void run() {
try {
lock.readLock().lock();
System.out.println(Thread.currentThread().getName()+" have read data: "+data);
} catch (Exception e) {
e.printStackTrace();
}finally{
lock.readLock().unlock();
}
try {
lock.writeLock().lock();
data = data-1;
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
}
}).start();
}
}
}
Thread-11 have read data: 991
Thread-13 have read data: 991
Thread-10 have read data: 991
Thread-14 have read data: 991
Thread-12 have read data: 991
Thread-23 have read data: 991
Thread-29 have read data: 988
Thread-18 have read data: 988
Thread-31 have read data: 986
Thread-32 have read data: 986
Thread-20 have read data: 986
Thread-33 have read data: 986
Thread-19 have read data: 986
Thread-25 have read data: 980
Thread-37 have read data: 978
可以看出来了吧
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/weixin_45203607/article/details/120236945
内容来源于网络,如有侵权,请联系作者删除!