zoukankan      html  css  js  c++  java
  • 秋招之路5:java多线程初探

    线程状态图

    Object 对象中的 wait()和notify()是用来实现实现等待 / 通知模式。
    其中等待状态和阻塞状态是不同的。
    等待状态的线程可以通过notify() 方法唤醒并继续执行,而阻塞状态的线程则是等待获取新的锁。
    调用 wait()方法后,当前线程会进入等待状态,直到其他线程调用notify()或notifyAll() 来唤醒。
    调用 notify() 方法后,可以唤醒正在等待的单一线程。
    thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。
    比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。

    需要注意的是:
    线程的资源有不少,但应该包含CPU资源和锁资源这两类。
    sleep(long mills):让出CPU资源,但是不会释放锁资源。
    wait():让出CPU资源和锁资源。
    锁是用来线程同步的,sleep(long mills)虽然让出了CPU,但是不会让出锁,其他线程可以利用CPU时间片了;
    但如果其他线程要获取sleep(long mills)拥有的锁才能执行,则会因为无法获取锁而不能执行,继续等待。
    但是那些没有和sleep(long mills)竞争锁的线程,一旦得到CPU时间片即可运行了。

    补充一点,阻塞和自旋的区别[同步模式的两个阵营]

    阻塞:阻塞锁的优势在于,阻塞的线程不会占用 CPU 时间, 不会导致 CPU 占用率过高,但进入时间以及恢复时间都要比自旋锁略慢。
    在竞争激烈的情况下阻塞锁的性能要明显高于自旋锁。在线程竞争不激烈的情况下使用自旋锁,竞争激烈的情况下使用阻塞锁。

    自旋:第一个线程加锁后,如果第二个线程也来加锁,就会一直在 while 中循环,直到第一个线程解锁后,第二个线程才能开始真正开始执行。
    自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。但当线程数不停增加时,性能下降明显,因为每个线程都需要执行,占用CPU时间。如果线程竞争不激烈,并且保持锁的时间段。适合使用自旋锁。

    并发的三个特性

    1. 原子性: 即一个操作或者多个操作,要么全部执行并且执行过程不被任何因素所打断,要不就都不执行
    2. 有序性: 即程序执行的顺序按照代码的先后顺序执行,不进行指令的重排列。
    3. 可见性: 指多个线程访问同一变量时,一个线程修改了这个变量的值,其他线程能够立即看到修改的值。

    synchronized 实现原理?

    首先说一下synchronized的作用:保证方法或者代码块在运行时,同一时刻只有一个进程可以进行访问,同时他还可以保证共享变量的内存可见性。
    其基本实现代码为:

    //对于普通同步方法,锁的是当前实例对象
    public class TestSyn{
      private int i=0;
      public synchronized void incr(){
        i++;
      }
    }
    
    //对于静态同步方法,锁是Class对象,也就是这一类的对象都进不来
    public class TestSyn{
      private int i=0;
      public static synchronized void incr(){
        i++;
      }
    }
    
    //对于同步代码块,锁是同步代码块里的对象
    public class TestSyn{
      private  int i=0;
      Object o = new Object();
      public  void incr(){
        synchronized(o){
            i++;
        }
      }
    }
    

    对于同步代码块:monitorenter 指令插入到同步代码块的开始位置,monitorexit指令插入到同步代码块的结束位置,
    JVM需要保证每一个monitorenter都有一个monitorexit与之相对应。任何一个对象都有一个monitor与之相关联[下面三张图可以很好理解,为什么与之相关联]



    当一个monitor被持有之后,它将被处于锁的状态。其他线程执行到monitorenter指令时,将尝试获取对象对应的monitor所有权,即尝试获取对象的锁。

    对于同步方法,方法的同步没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现),
    不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。

    JVM就是根据这个标识符来判断是不是同步方法的,当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置。
    如果是同步方法,进而按照上面同步代码块的执行。
    synchronized 是重量级锁,在 JDK1.6 中进行优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。
    下面有一个锁升级,讲得特别好的链接
    [https://www.cnblogs.com/wyc1994666/p/11748212.html#1基本用法]

    Java 内存模型(JMM)简短总结(具体在6中)

    JMM 规定了线程的工作内存和主内存的交互关系,以及线程之间的可见性和程序的执行顺序。

    • 一方面,要为程序员提供足够强的内存可见性保证。
    • 另一方面,对编译器和处理器的限制要尽可能地放松。
      JMM 对程序员屏蔽了 CPU 以及 OS 内存的使用问题,能够使程序在不同的 CPU 和 OS 内存上都能够达到预期的效果。

    Java 采用内存共享的模式来实现线程之间的通信(线程之间不能直接通信)。

  • 相关阅读:
    Python写出LSTM-RNN的代码
    TensorFlow 实现 RNN 入门教程
    RNN与应用案例:注意力模型与机器翻译
    RNN入门
    内积(又名点积)
    词袋模型(BOW, bag of words)
    softmax
    Dropout
    随机梯度下降法
    L1范式和L2范式
  • 原文地址:https://www.cnblogs.com/whyaza/p/12325782.html
Copyright © 2011-2022 走看看