望着时间滴答滴答的流过,我不曾改变过 . . .

秋招之路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 采用内存共享的模式来实现线程之间的通信(线程之间不能直接通信)。

posted @ 2020-02-18 22:23  whyaza  阅读(130)  评论(0编辑  收藏  举报