一、 synchronized的缺陷
当一个代码块被synchronized修饰时,同时该代码块被一个线程执行,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
1)获取锁的线程执行完成代码块,自动释放锁资源
2)代码块中执行异常,由jvm释放锁资源
如果在执行代码块时候,一个线程在代码块执行过程中Thread.sleep() 睡眠 线程被阻塞了,其他线程只能等待当前线程执行完成后才能执行。及其浪费效率。
另外当多个线程在执行写与写操作时,会发生线程冲突,但当多个线程用于读操作,其中一个线程读取占用的锁资源 而其他线程只能等待造成浪费资源。
通过多个线程读取操作线程不会冲突,通过lock可以实现。
不同点:
synchronized是java内置的,是java关键字,lock 是一个接口 不是内置的通过这个接口可以实现同步访问
synchronized不需要用户手动释放锁资源,当同步方法或者同步代码块执行完成后,系统会自动让线程释放锁资源。而Lock必须要手动释放锁资源,如果不释放会出现死锁
二、 java.util.concurrent.locks包下常用的类
1) Lock 是一个接口
提供了六个方法
void lock(); boolean tryLock(); boolean tryLock(long time,timeUnit unit); void lockInteruptibly(); contition newContition() void unlock();
其中lock() tryLock() tryLock(long time,timeUnit unit) lockInteruptibly() 用于获取锁 unlock() 用于释放锁
由于采用lock 不会自动释放锁,需要手动释放锁 所以采用lock 必须在try{}catch(){}finally{}异常捕获中进行,在finally中释放锁
lock()是最常用的获取锁的方式
例子:
Lock lock = ..
lock.lock();
try{
}catch(Exception e){
}finally{
lock.unlock()
}
tryLock 尝试获取锁,如果获取到返回true 如果获取不到返回false 不会拿不到锁一致在等待
tryLock(long time,TimeUnit unit) 与tryLock()一样都是尝试获取锁,如果获取到返回true 否则返回false 区别在于tryLock(long time ,TimeUnit unit)会等待一定时间,如果直接获取到锁返回true 或者在设置的等待时间内获取到锁返回true 否则返回flase
Lock lock = ..
if(lock.tryLock()){
try{
}catch(){
}finally{
lock.unlock();
}
}
lockInterruptibly() 可以用来获取锁,但是如果在获取的锁已经被其他线程锁占用 在等待过程中可以中断当前线程的等待状态。通俗的将 如果 a b两个线程通过Lock.lockInterruptibly()获取某一个锁时,如果a先获取到了锁 b处于等待状态 那么b可以调用b.interrup()方法中断b的等待状态
由于lockInterruptibly()的声明中抛出了异常,所以lock.lockInterruptibly()必须放在try块中或者在调用lockInterruptibly()的方法外声明抛出InterruptedException。
public void method () throws InterruptedException{
lock.Interruptibly();
try{
}catch(){
}finally{
lock.unlock();
}
}
当一个线程获取到了锁 是不会被interrup()方法中断 只有处于等待状态的线程在才能被中断。
2)ReentrantLock 可重入锁
ReentrantLock是Lock接口的唯一实现类
Lock 接口的实现方式:
package cn.ac.bcc.lock; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockDemo { private List<Integer> list = new ArrayList<>(); public static void main(String[] args) { //实例化当前类对象 final LockDemo ld = new LockDemo(); //创建两个线程对象 调用insert方法 new Thread(new Runnable(){ @Override public void run() { ld.insert(Thread.currentThread()); } }).start(); new Thread(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub ld.insert(Thread.currentThread()); } }).start(); } public void insert(Thread thread){ //创建局部lock锁对象 Lock lock = new ReentrantLock(); System.out.println(lock); //获取锁 lock.lock(); try { for(int i=0;i<5;i++){ list.add(i); } System.out.println(Thread.currentThread().getName()+"获取了锁"); } catch (Exception e) { // TODO: handle exception }finally{ //释放锁 lock.unlock(); System.out.println(Thread.currentThread().getName()+"释放了锁"); } } }
运行结果:
java.util.concurrent.locks.ReentrantLock@100363[Unlocked]
java.util.concurrent.locks.ReentrantLock@e0a386[Unlocked]
Thread-0获取了锁
Thread-1获取了锁
Thread-0释放了锁
Thread-1释放了锁
产生这种结果的原因: 当多个线程获取同一个对象的方法中的局部变量,每一个线程都会获取一个局部变量lock的副本 通过打印锁对象地址可以发现当前锁对象是不同的锁 所以他们可以获取不同的锁
解决这种方案:
将获取锁对象成员变量 属于当前对象
package cn.ac.bcc.lock; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockDemo { private List<Integer> list = new ArrayList<>(); //创建局部lock锁对象 private Lock lock = new ReentrantLock(); public static void main(String[] args) { //实例化当前类对象 final LockDemo ld = new LockDemo(); //创建两个线程对象 调用insert方法 new Thread(new Runnable(){ @Override public void run() { ld.insert(Thread.currentThread()); } }).start(); new Thread(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub ld.insert(Thread.currentThread()); } }).start(); } public void insert(Thread thread){ System.out.println(lock); //获取锁 lock.lock(); try { for(int i=0;i<5;i++){ list.add(i); } System.out.println(Thread.currentThread().getName()+"获取了锁"); } catch (Exception e) { // TODO: handle exception }finally{ //释放锁 lock.unlock(); System.out.println(Thread.currentThread().getName()+"释放了锁"); } } }
运行结果:
java.util.concurrent.locks.ReentrantLock@bcda2d[Unlocked]
java.util.concurrent.locks.ReentrantLock@bcda2d[Locked by thread Thread-0]
Thread-0获取了锁
Thread-0释放了锁
Thread-1获取了锁
Thread-1释放了锁
tryLock()
package cn.ac.bcc.lock; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockDemo { private List<Integer> list = new ArrayList<>(); //创建局部lock锁对象 private Lock lock = new ReentrantLock(); public static void main(String[] args) { //实例化当前类对象 final LockDemo ld = new LockDemo(); //创建两个线程对象 调用insert方法 new Thread(new Runnable(){ @Override public void run() { ld.insert(Thread.currentThread()); } }).start(); new Thread(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub ld.insert(Thread.currentThread()); } }).start(); } public void insert(Thread thread){ System.out.println(lock); //获取锁 if(lock.tryLock()){ try { for(int i=0;i<5;i++){ list.add(i); } System.out.println(Thread.currentThread().getName()+"获取了锁"); } catch (Exception e) { // TODO: handle exception }finally{ //释放锁 lock.unlock(); System.out.println(Thread.currentThread().getName()+"释放了锁"); } }else{ System.out.println(Thread.currentThread().getName()+"没有获取到锁"); } } }
运行结果:
java.util.concurrent.locks.ReentrantLock@14e8cee[Unlocked]
java.util.concurrent.locks.ReentrantLock@14e8cee[Locked by thread Thread-0]
Thread-1没有获取到锁
Thread-0获取了锁
Thread-0释放了锁
当多个线程获取同一个锁对象时,当线程a 获取锁 b尝试获取锁失败返回false
lockInterruptibly()
package cn.ac.bcc.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class InterruptiblyDemo { Lock lock = new ReentrantLock(); public static void main(String[] args) { InterruptiblyDemo interruptiblyDemo = new InterruptiblyDemo(); MyThread myThread = new MyThread(interruptiblyDemo); MyThread myThread2 = new MyThread(interruptiblyDemo); myThread.start(); myThread2.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //中断线程2 myThread2.interrupt(); } public void insert() throws InterruptedException{ lock.lockInterruptibly(); try{ System.out.println(Thread.currentThread().getName()+"得到了锁"); long startTime = System.currentTimeMillis(); for(;;){ if(System.currentTimeMillis()-startTime>=Integer.MAX_VALUE){ break; } } }catch(Exception e){ e.printStackTrace(); }finally{ System.out.println(Thread.currentThread().getName()+"释放了锁"); lock.unlock(); } } } class MyThread extends Thread{ InterruptiblyDemo interruptiblyDemo; public MyThread(InterruptiblyDemo interruptiblyDemo){ this.interruptiblyDemo=interruptiblyDemo; } @Override public void run(){ try { interruptiblyDemo.insert(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println(Thread.currentThread().getName()+"锁被中断"); } } }
运行结果:
Thread-0得到了锁
java.lang.InterruptedException
Thread-1锁被中断
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:896)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
at cn.ac.bcc.lock.InterruptiblyDemo.insert(InterruptiblyDemo.java:30)
at cn.ac.bcc.lock.MyThread.run(InterruptiblyDemo.java:60)
https://www.cnblogs.com/baizhanshi/p/6419268.html