Java锁

作者: whupenger | 来源:发表于2019-03-12 15:50 被阅读0次

    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锁

          本文链接:https://www.haomeiwen.com/subject/uyegpqtx.html