1.锁是干什么用的
锁一般来说用作资源控制,限制资源访问,防止在并发环境下造成数据错误
2.重入锁
重入锁也叫作递归锁,指的是同一个线程外层函数获取到一把锁后,内层函数同样具有这把锁的控制权限
synchronized和ReentrantLock就是重入锁对应的实现
synchronized重量级的锁
ReentrantLock轻量级的锁 lock()代表加入锁 unlock()代表释放锁
不可重入锁:说明当没有释放该锁时。其他线程获取该锁会进行等待
public class MyLock { //标识锁是否可用 如果值为 true代表有线程正在使用该 锁 ,如果为false代表没有人使用锁 private boolean isLocked=false; //获取锁:加锁 public synchronized void lock() throws InterruptedException { //判断当前该锁是否正在使用 while (isLocked){ wait(); } //当前没有人使用情况 下就占用该锁 isLocked=true; } //释放锁 public synchronized void unLock(){ //将当前锁资源释放 isLocked=false; //唤起正在等待使用锁的线程 notify(); } }
public class MyLockTest {
MyLock myLock=new MyLock(); //A业务 public void print() throws InterruptedException { //获取一把锁 myLock.lock(); System.out.println("print业务方法"); doAdd(); //释放锁 myLock.unLock(); } //B业务方法 public void doAdd() throws InterruptedException { //获取一把锁 myLock.lock(); System.out.println("aoAdd方法"); //释放锁 myLock.unLock(); } public static void main(String[] args) throws InterruptedException { MyLockTest myLockTest=new MyLockTest(); myLockTest.print(); }
}
控制台结果:
当前效果就造成了死锁
synchronized可重入性:如果当前A持有一把锁,在A业务内部调用B,那么B也同样拥有这把锁的使用权限
编写测试代码:
MyLock myLock=new MyLock(); //A业务 public synchronized void print() throws InterruptedException { System.out.println("print业务方法"); doAdd(); } //B业务方法 public synchronized void doAdd() throws InterruptedException { System.out.println("aoAdd方法"); } public static void main(String[] args) throws InterruptedException { MyLockTest myLockTest=new MyLockTest(); myLockTest.print(); }
控制台结果:
ReentrantLock同样具有可重入性
编写测试代码:
public class MyLockTest { //创建锁对象 Lock lock=new ReentrantLock(); //A业务 public void print() throws InterruptedException { //获取了一把锁 lock.lock(); System.out.println("print业务方法"); doAdd(); //释放锁 lock.unlock(); } //B业务方法 public void doAdd() throws InterruptedException { //获取一把锁 lock.lock(); System.out.println("aoAdd方法"); //释放锁 lock.unlock(); } public static void main(String[] args) throws InterruptedException { MyLockTest myLockTest=new MyLockTest(); myLockTest.print(); } }
控制台结果:
3. 读写锁
并发线程下,所有线程都执行读的操作,会不会有问题
并发线程下,部分读部分写会不会有问题 会发生写冲突
并发线程下,所有线程都执行写会不会有问题 会发生写冲突
编写测试代码:
//创建一个集合 static Map<String,String> map=new HashMap<String,String>(); //创建一个读写锁 static ReentrantReadWriteLock lock=new ReentrantReadWriteLock(); //获取读锁 static Lock readLock=lock.readLock(); //获取写锁 static Lock writeLock=lock.writeLock(); //写操作 public Object put(String key,String value){ writeLock.lock(); try { System.out.println("Write正在执行写操作~"); Thread.sleep(100); String put = map.put(key, value); System.out.println("Write写操作执行完毕~"); return put; } catch (InterruptedException e) { e.printStackTrace(); }finally { writeLock.unlock(); } return null; } //写操作 public Object get(String key){ readLock.lock(); try { System.out.println("Read正在执行读操作~"); Thread.sleep(100); String value = map.get(key); System.out.println("Read读操作执行完毕~"); return value; } catch (InterruptedException e) { e.printStackTrace(); }finally { readLock.unlock(); } return null; } public static void main(String[] args) { ReadWriteLock lock=new ReadWriteLock(); for (int i = 0; i < 10; i++) { int finalI = i; new Thread(()->{ try { //写操作 lock.put(finalI +"","value"+finalI); //读操作 System.out.println(lock.get(finalI+"")); } catch (Exception e) { e.printStackTrace(); } }).start(); } }
控制台 结果:
我们可以看出是当所有写操作都执行完毕后才开始执行读操作
4. 乐观锁
总认为不会发生并发问题,每一次取数据时总认为其他线程不会对该数据先进性更改,但是在更新时会判断其他线程在这之前有
没有对该数据进行修改,
数据库当中常用方案:版本号控制
5.悲观锁
总是假设最坏的情况,每次取数据时,都会认为其他线程会对该数据进行修改,所以会进行加锁
其他线程访问的时候会阻塞等待,例如在数据库当中可以使用行锁,表锁以及读写锁等方式实现