Java锁

Thread几个方法

a、start()方法,调用该方法开始执行该线程;
b、stop()方法,调用该方法强制结束该线程执行;
c、join方法,调用该方法等待该线程结束。
d、sleep()方法,调用该方法该线程进入等待。
e、run()方法,调用该方法直接执行线程的run()方法,但是线程调用start()方法时也会运行run()方法,区别就是一个是由线程调度运行run()方法,一个是直接调用了线程中的run()方法

wait()与notify()方法是Object的方法,不是Thread的方法。同时,wait()与notify()会配合使用,分别表示线程挂起和线程恢复

wait()与sleep()的区别,简单来说wait()会释放对象锁而sleep()不会释放对象锁。

线程状态

  • 新建状态:新建线程对象,并没有调用start()方法之前
  • 就绪状态:调用start()方法之后线程就进入就绪状态,但是并不是说只要调用start()方法线程就马上变为当前线程,在变为当前线程之前都是为就绪状态。值得一提的是,线程在睡眠和挂起中恢复的时候也会进入就绪状态哦。
  • 运行状态:线程被设置为当前线程,开始执行run()方法。就是线程进入运行状态
  • 阻塞状态:线程被暂停,比如说调用sleep()方法后线程就进入阻塞状态
  • 死亡状态:线程执行结束

锁类型

  • 可重入锁:在执行对象中所有同步方法不用再次获得锁,synchronized 和 ReentrantLock 都是可重入锁,可重入锁可以在一定程度避免死锁。
  • 可中断锁:在等待获取锁过程中可中断
  • 公平锁/非公平锁: 公平锁指多个线程按照申请锁的顺序来获取锁,非公平锁就是没有顺序完全随机,所以能会造成优先级反转或者饥饿现象;synchronized 就是非公平锁,ReentrantLock(使用 CAS 和 AQS 实现) 通过构造参数可以决定是非公平锁还是公平锁,默认构造是非公平锁;非公平锁的吞吐量性能比公平锁大好
  • 读写锁:对资源读取和写入的时候拆分为2部分处理,读的时候可以多线程一起读,写的时候必须同步地写
  • Java加锁有两种:
    • Synchronized关键字
    • 显示Lock

Synchronized

是Java的一个关键字,它能够将代码块(方法)锁起来

  public synchronized void test() {
        // doSomething
    }

synchronized是一种互斥锁,一次只能允许一个线程进入被锁住的代码块
synchronized是一种内置锁/监视器锁,Java中每个对象都有一个内置锁(监视器,也可以理解成锁标记),而synchronized就是使用对象的内置锁(监视器)来将代码块(方法)锁定的

  • synchronized保证了线程的原子性。(被保护的代码块是一次被执行的,没有任何线程会同时访问)
  • synchronized还保证了可见性。(当执行完synchronized之后,修改后的变量对其他的线程是可见的)
  • synchronized实现原理:
    synchronized是悲观锁,在字节码层被映射成两个指令:monitorentermonitorexit,当一个线程遇到monitorenter指令时,会尝试去获取锁,如果获取成功,锁的数量+1,(因为synchronized是一个可重入锁,需要使用锁计数来判断锁的情况),如果没有获取到锁,就会阻塞;当线程遇到monitorexit指令时,锁计数-1,当计数器为0时,线程释放锁;如果线程遇到异常,虚拟机会释放锁

Lock

Lock完全用Java写成,在java这个层面是无关JVM实现的。在java.util.concurrent.locks包中有很多Lock的实现类,常用的有ReentrantLockReadWriteLock(实现类ReentrantReadWriteLock),其实现都依赖java.util.concurrent.AbstractQueuedSynchronizer类,实现思路都大同小异,因此我们以ReentrantLock作为讲解切入点

  • lock是一个java接口,一般使用ReentrantLock这个实现类。
  • 经过观察ReentrantLock把所有Lock接口的操作都委派到一个Sync类上,该类继承了AbstractQueuedSynchronizer
  private final Sync sync;

    abstract static class Sync extends AbstractQueuedSynchronizer
 public void lock() {
        sync.lock();
    }

 public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

使用lock显示加锁,可以判断锁的状态,有多种获取锁的方式,获取失败的线程不一定需要阻塞

 //需要参与同步的方法
    private void method(Thread thread){
        lock.lock();
        try {
            System.out.println("线程名"+thread.getName() + "获得了锁");
        }catch(Exception e){
            e.printStackTrace();
        } finally {
            System.out.println("线程名"+thread.getName() + "释放了锁");
            lock.unlock();
        }
    }

  • lock实现原理:
    lock是使用CAS乐观锁(CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。)

AQS,抽象队列同步器

AQS即AbstractQueuedSynchronizer的缩写,是并发编程中实现同步器的一个框架,它在内部定义了一个int state变量,用来表示同步状态
AQS基于一个FIFO双向队列实现,被设计给那些依赖一个代表状态的原子int值的同步器使用,在AQS中即为一个叫state的int值,该值通过CAS进行原子修改。

  • 在AQS中存在一个FIFO队列,队列中的节点表示被阻塞的线程,队列节点元素有4种类型, 每种类型表示线程被阻塞的原因,这四种类型分别是:

    • CANCELLED : 表示该线程是因为超时或者中断原因而被放到队列中
    • CONDITION : 表示该线程是因为某个条件不满足而被放到队列中,需要等待一个条件,直到条件成立后才会出队
    • SIGNAL : 表示该线程需要被唤醒
    • PROPAGATE : 表示在共享模式下,当前节点执行释放release操作后,当前结点需要传播通知给后面所有节点
  • 由于一个共享资源同一时间只能由一条线程持有,也可以被多个线程持有,因此AQS中存在两种模式,如下

    • 独占模式: 独占模式表示共享状态值state每次只能由一条线程持有,其他线程如果需要获取,则需要阻塞,如JUC中的ReentrantLock
    • 共享模式:共享模式表示共享状态值state每次可以由多个线程持有,如JUC中的CountDownLatch

你可能感兴趣的:(Java锁)