各种锁的概念

我们学习多线程相关知识的时候都会涉及到各种锁,自旋锁、乐观锁、悲观锁、可重入锁、互斥锁、排它锁、独占锁、 共享锁。。等等
其实很多锁的概念都是来源与数据库,java中的锁也有这些特性,因此我们在学习多线程的时候就会接触这些概念

synchronized

我们先从这个关键字开始讲,相信接触过多线程的人都知道这个关键字是用来干嘛的,它保证了我们一个代码块的原子性,当一个线程想要执行synchronized中的代码的时候,就要先获取一把锁,只要获取到了这把锁才能执行这个synchronized中的代码,如果获取不到就会阻塞(并不会立刻阻塞,后面介绍)其实synchronized就是一把 悲观锁、自旋锁、互斥锁、排它锁、可重入锁、独占锁

一个synchronized就对应这么多锁的概念,下面我们来具体介绍下这些锁的概念

悲观锁

悲观锁其实就业是持保守状态,每次执行一段代码它都会觉得会引发线程安全问题,故每次都上锁,就像synchronized一样,每次执行synchronized的代码块都会先上锁

互斥锁

当一个线程获取到该锁之后,其他线程就获取不了这把锁了,它保证在任何时候都只有一个线程访问该对象(synchronized的代码块)

排它锁

和互斥锁的概念差不多,比如一个线程A去给一个代码块加上了X锁,那么只有当对象A释放了这个X锁之后其他线程才能给这个代码块加上其他的锁,即一个线程给一个代码块加上了锁之后,在这个线程没有释放该锁之前不允许其他线程给这个代码块加上其他任何锁

可重入锁

我们先来看一个案例

	public void testSynchronized(){
      test1();
  }

   public synchronized void test2(){
      System.out.println("test2");

   }

   public synchronized void test1(){
      System.out.println("test1");
      test2();

   }



我们在testSynchronized()方法里调用了test1()但是执行test1()的时候是要加锁的,当前锁就是该this对象,但是我们在 test1()中又调用了 test2() 同样 想执行test2() 就要先获取 this这把锁,但是我们当前线程已经获取到了 this这个锁,而且也没有释放,等于this这个锁已经被占用了,那么我们怎么还能获取this这个锁呢? java对象中会有一个两个值记录这把锁,一个是获取这把锁的线程,一个是一个计数器,就拿上面这个来说,当我们获取到了 this这把之后 java对象中会记录获取这把锁的线程,计数器+1 当 当前线程再次去执行一个也要这个锁的代码块的时候 虚拟机一看当前线程已经获取到了这把锁,于是就把 计数器+1就行了 这个计数器就是记录着重入的次数

独占锁

这把锁只能被一个线程拥有 这个好理解,即有一个线程获取到了该锁之后在这个线程没有释放该锁之前,其他线程是获取不到这个锁的

自旋锁

java并发包中有很多类多支持 CAS 例如AtomicInteger类等等
这里简单的介绍下CAS Compare and Swap 它是这三个单词的首字母,比较并且交换,当我们要更新一个值的时候 我们给他两个值 一个要比较的值 (我们假如是 20) 一个是要更新的值 (假如是 30)
当我们去更新一个字段的时候 它会先比较这个字段的值 是不是等于20如果等于20那么更新成30并且返回 true代码更新成功

我们来看下 Random类中的next这个方法实现

   protected int next(int bits) {
        long oldseed, nextseed;
        AtomicLong seed = this.seed;
        do {
            oldseed = seed.get();
            nextseed = (oldseed * multiplier + addend) & mask;
        } while (!seed.compareAndSet(oldseed, nextseed));
        return (int)(nextseed >>> (48 - bits));
    }


其他代码看不懂没关系,我们主要看那个 do…while循环。其实这就是一个自旋锁,当我们使用seed.compareAndSet(oldseed, nextseed)方法去更新这个值的时候,如果更新不成功,则重新获取最新的值再去进行更新,直到更新成功为止

为什么synchronized是自旋锁

上面我们说到当一个线程获取到这个锁之后其他线程再去获取这把锁的时候就获取不到了,那么该线程就会阻塞,我们也说了,不会立刻阻塞,我们知道当让一个线程阻塞会让该线程的状态从用户态变成内核态,唤醒的时候又要从内核态转换成用户态,那么这个转换是有性能开销的,所以java虚拟机针对synchronized做了优化,虚拟机认为,获得该锁的线程可能会很快就执行完释放该锁,所以当我们一个线程获取不到这个锁的时候并不会立刻阻塞,而是会自旋10次(默认是十次)如果10次之后还获取不到该锁那就会阻塞

乐观锁

即每次操作都不加锁,持比较乐观的状态认为不会引发线程安全问题,CAS就是一种乐观锁

共享锁

顾名思义,可以有多个线程获取该锁,java中ReadWriteLock就是一把共享锁

你可能感兴趣的:(多线程)