農農  

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.悲观锁
  总是假设最坏的情况,每次取数据时,都会认为其他线程会对该数据进行修改,所以会进行加锁
  其他线程访问的时候会阻塞等待,例如在数据库当中可以使用行锁,表锁以及读写锁等方式实现

 

6. CAS无锁模式

  6.1  什么 是CAS

  CAS:Compare and Swap,即比较再交换。

 

  jdk5增加了并发包java.util.concurrent.*,其下面的类使用CAS算法实现了区别于synchronouse同步锁的一种乐观锁。

  JDK 5之前Java语言是靠synchronized关键字保证同步的,这是一种独占锁,也是是悲观锁。

 

  6.2  Java内存模型:JMM(Java Memory Model)

    在内存模型当中定义了一个主内存,所有声明的实例变量都存在于主内存当中,主内存的数据会共享给所有线程,每一个线程有一块工作内存,工作内存当中主内存数据的副本
    当更新数据时,会将工作内存中的数据同步到主内存当中

  6.2 CAS无锁机制

    本身无锁,采用乐观锁的思想,在数据操作时对比数据是否一致,如果一致代表之前没有线程操作该数据,那么就会更新数据,如果不一致代表有县城更新则重试
  CAS当中包含三个参数CAS(V,E,N),V标识要更新的变量,E标识预期值,N标识新值

  

  运行过程:
  1.线程访问时,先会将主内存中的数据同步到线程的工作内存当中
  2.假设线程A和线程B都有对数据进行更改,那么假如线程A先获取到执行权限
  3.线程A先会对比工作内存当中的数据和主内存当中的数据是否一致,如果一致(V==E)则进行更新,不一致则刷新数据,重新循环判断
  4.这时更新完毕后,线程B也要进行数据更新,主内存数据和工作内存数据做对比,如果一致则进行更新,不一致则将主内存数据重新更新到工作内存,然后循环再次对比两个内存中的数据
  直到一致为止


  CAS无锁机制存在一个问题
  ABA问题,如果将原来A的值改为了B,然后又改回了A,虽然最终结果没有发生改变,但是在过程中是对该数据进行了修改操作
  解决该问题:在Java中并发包下有一个原子类:AtomicStampedReference,在该类当中通过版本控制判断值到底是否被修改
  解释:如果对值进行了更改则版本号+1,那么在CAS当中不仅仅对比变量的值,还要对比版本号,如果值和版本号都相等则代表没有被修改,如果有一方不相等代表进行过更改
  那么就从主内存中重新刷新数据到工作内存然后循环对比,直到成功为止~

 

  7.保证线程安全的三个方面:
  1.原子性:保证同一时刻该资源只能有一个线程访问修改,其他线程阻塞等待,例如Atomic包,锁
  2.可见性:一个线程对于主内存的数据操作对于其他线程是可见的
  3.有序性:一个线程观察其他线程中指令执行顺序,由于指令重排序存在,观察结果一般杂乱无序
  原子性: 互斥访问,Atomic包,CAS算法,Synchronized,Lock

  可见性:synchronized,volatile

  顺序性:happends-before

 

  8.  原子类

   编写测试代码:  

 //定义一个原子类对象
    private AtomicInteger  atomicInteger=new AtomicInteger();
    public  void getCount(){
        //+1再返回
        System.out.println(atomicInteger.incrementAndGet());
    }

    public static void main(String[] args) {
        AtomicTest atomicTest=new AtomicTest();
        for (int i=1;i<=2;i++){
            new Thread(()->{
                for (int j=1;j<=100;j++){
                    atomicTest.getCount();
                }
            }).start();
        }
    }

 

  控制台效果:

 

 

 

  8.AQS:全成AbstractQueueSynchronizer,抽象队列同步器,这个类在java.util.concurrent.locks包下
    它是一个底层同步工具类,比如CountDownLatch,Sammphore,ReentrantLock,ReentrantReadWriteLock等等都是基于AQS
    底层三个内容:
    1.state(用于计数器)
    2.线程标记(哪一个线程加的锁)
    3.阻塞队列(用于存放阻塞线程)

 

posted on 2020-03-25 16:15  Baekhyunne  阅读(319)  评论(0编辑  收藏  举报