编程导航Java并发编程之美话题讨论

Java并发编程之美

3 参与
分享

快来分享你的内容吧~

点击登录,快来和大家讨论吧~
表情
图片
话题
打卡
综合
交流
文章
问答

java并发编程之美第三章第四章读书笔记

<html> <head></head> <body> <div class="content ql-editor"> <p><br></p> <p><br></p> <p><br></p> <h1>java并发包中ThreadLocalRandom类原理剖析</h1> <p><br></p> <p>该ilei是JDK7在JUC包下面新增的随机数生成器吗,弥补了Random类在多线程下的缺陷</p> <p><br></p> <h2>Random类及其局限性</h2> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class RandomTest { </div> <div class="ql-code-block"> public static void main(String[] args) { </div> <div class="ql-code-block"> Random random=new Random(); </div> <div class="ql-code-block"> for (int i=0;i&lt;10;i++){ </div> <div class="ql-code-block"> System.out.println(random.nextInt(5)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p>每个Random实例里面都有一个原子性的种子变量来记录当前的种子值,当要生成的随机数需要根据当前种子计算新的种子并更新回原子变量,在多线程下使用单个Random实例生成随机数时,当多个线程同时计算随机数来计算新的种子时,多个线程会竞争同一个原子变量的更新操作,由于原子变量的更新是CAS操作,同时只有一个线程会成功,所以造成大量线程进行自旋操作,这会降低并发性能</p> <p><br></p> <h2>ThreadLocalRandom</h2> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class ThreadLocalRandomTest { </div> <div class="ql-code-block"> public static void main(String[] args) { </div> <div class="ql-code-block"> ThreadLocalRandom random=ThreadLocalRandom.current(); </div> <div class="ql-code-block"> for (int i=0;i&lt;10;i++){ </div> <div class="ql-code-block"> System.out.println(random.nextInt(5)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p>每个线程都维护一个种子变量,则每个变量生成随机数时候根据自己老的种子计算新的种子,并使用新种子更新老的种子,再根据种子计算随机数,就不会存在竞争问题,大大提高了高并发性能</p> <p><br></p> <h2>源码分析</h2> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1609216259264610306/vilme8e6.jpeg"></p> <p>从图中可以看出ThreadLocalRandom类继承了Random 类并重写了nextlnt方法、在 ThreadLocalRandom 类中并没有使用继承自Random 类的原子性种子变量ThreadLocalRandom 中并没有存放具体的种子,具体的种子存放在具体的调用线程threadLocalRandomSeed 变量里面。ThreadLocalRandom 类似于 ThreadLocal 类,就是工具类。当线程调用 ThreadLocalRandom 的current 方法时,ThreadLocalRandom 负责初始调用线程的threadLocalRandomSeed 变量,也就是初始化种子。</p> <p>当调用ThreadLocalRandom 的 nextInt方法时,实际上是获取当前线程threadLocalRandomSeed 变量作为当前种子来计算新的种子,然后更新新的种子到当前线程的threadLocalRandomSeed 变量,而后再根据新种子并使用具体算法计算随机数。这需要注意的是,threadLocalRandomSeed 变量就是 Thread 类里面的一个普通 long变量,它并不是原子性变量。其实道理很简单,因为这个变量是线程级别的,所以根本不需要使用</p> <p>原子性变量,如果你还是不理解可以思考下 ThreadLocal 的原理其中 seeder 和 probeGenerator是两个原子性变量,在初始化调用线程的种子和探针变量时会用到它们,每个线程只会使用一次。</p> <p>另外,变量instance 是 ThreadLocalRandom 的一个实例,该变量是 static 的。当多线程通过 ThreadLocalRandom 的 current 方法获取ThreadLocalRandom 的实例时,其实获取的是同一个实例。但是由于具体的种子是存放在线程里面的,所以在 ThreadLocalRandom的实例里面只包含与线程无关的通用算法,所以它是线程安全的</p> <p><br></p> <h3>Unsafe机制</h3> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1609216259264610306/9epu82ey.jpeg"></p> <p><br></p> <h3>ThreadLocalRandom 的current()方法</h3> <p><br></p> <p>该方法是获取ThreadLocalRandom实例,并初始化调用线程中的ThreadLocalRandomSeed和ThreadLocalRandomProbe变量</p> <div class="ql-code-block-container"> <div class="ql-code-block"> static final ThreadLocalRandom instance = new ThreadLocalRandom(); </div> <div class="ql-code-block"> public static ThreadLocalRandom current() { </div> <div class="ql-code-block"> if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0) </div> <div class="ql-code-block"> localInit(); </div> <div class="ql-code-block"> return instance; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> static final void localInit() { </div> <div class="ql-code-block"> int p = probeGenerator.addAndGet(PROBE_INCREMENT); </div> <div class="ql-code-block"> int probe = (p == 0) ? 1 : p; // skip 0 </div> <div class="ql-code-block"> long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT)); </div> <div class="ql-code-block"> Thread t = Thread.currentThread(); </div> <div class="ql-code-block"> UNSAFE.putLong(t, SEED, seed); </div> <div class="ql-code-block"> UNSAFE.putInt(t, PROBE, probe); </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h3>int nextInt(int bound)方法</h3> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> public int nextInt(int bound) { </div> <div class="ql-code-block"> if (bound &lt;= 0) </div> <div class="ql-code-block"> throw new IllegalArgumentException(BadBound); </div> <div class="ql-code-block"> int r = mix32(nextSeed()); </div> <div class="ql-code-block"> int m = bound - 1; </div> <div class="ql-code-block"> if ((bound &amp; m) == 0) // power of two </div> <div class="ql-code-block"> r &amp;= m; </div> <div class="ql-code-block"> else { // reject over-represented candidates </div> <div class="ql-code-block"> for (int u = r &gt;&gt;&gt; 1; </div> <div class="ql-code-block"> u + m - (r = u % bound) &lt; 0; </div> <div class="ql-code-block"> u = mix32(nextSeed()) &gt;&gt;&gt; 1) </div> <div class="ql-code-block"> ; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> return r; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> final long nextSeed(){ </div> <div class="ql-code-block"> Thread t; </div> <div class="ql-code-block"> long r; </div> <div class="ql-code-block"> UNSAFE.putLong(t=Thread.currentThread(),SEED,r=UNSAFE.getLong(t,SEED)+GAMMA); </div> <div class="ql-code-block"> return r; </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h2>总结</h2> <p><br></p> <p>首先讲解了Randon实现原理以及Random在多线程下需要竞争种子原子变量更新操作的缺点,从而引进ThreadLocalRandom类,该类使用ThreadLocal的原理,让每一个线程都持有一个本地的种子变量,该种子变量只有在使用随机数时候才会被初始化,在多线程下计算新种子是根据自己线程内维护的种子变量来进行更新,从而避免了竞争</p> <p><br></p> <h1>第四章java并发包中原子操作类原理剖析</h1> <p><br></p> <p>JUC包提供了一系列的原子类操作,这些类都是使用非阻塞算法CAS实现的,相比使用锁实现原子操作在性能上有很大提高</p> <p>本章只讲解最简单的AtomicLong类的实现原理以及JDK8中新增的LongAdder和LongAccumulator类的原理</p> <p><br></p> <h2>原子变量的操作类</h2> <p><br></p> <p>AtomicLong,AtomicInteger,AtomicBoolean等原子类操作类,内部使用Unsafe来实现</p> <p><br></p> <p><br></p> <p></p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class AtomicLongTest extends Number implements Serializable { </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static final long serialVersionUID= 1927816293512124184L; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static final Unsafe unsafe =Unsafe.getUnsafe(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static final long valueOffset; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> static final boolean VM_SUPPORTS_LONG_CAS= VMSupportsCS8(); </div> <div class="ql-code-block"> private static native boolean VMSupportsCS8(); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> static { </div> <div class="ql-code-block"> try{ </div> <div class="ql-code-block"> valueOffset = unsafe.objectFieldOffset(AtomicLongTest.class.getDeclaredField("value")); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> }catch (Exception e){ </div> <div class="ql-code-block"> throw new Error(e); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> private volatile long value; </div> <div class="ql-code-block"> private AtomicLongTest(long initiaValue){ </div> <div class="ql-code-block"> value=initiaValue; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public int intValue() { </div> <div class="ql-code-block"> return 0; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public long longValue() { </div> <div class="ql-code-block"> return 0; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public float floatValue() { </div> <div class="ql-code-block"> return 0; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public double doubleValue() { </div> <div class="ql-code-block"> return 0; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h4>递增和递减代码操作</h4> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1609216259264610306/1h6v1ji4.jpeg"></p> <h4>boolean compareAndSet(long expect,long update)</h4> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> public final boolean compareAndSet(long expect, long update) { </div> <div class="ql-code-block"> return unsafe.compareAndSwapLong(this, valueOffset, expect, update); </div> <div class="ql-code-block"> } </div> </div> <p>内部还是调用了 unsafe.compareAndSwapLong方法,如果原子变量中的value值等于expect,则使用update值更新该值并返回true,否则返回false</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class AtomicTest { </div> <div class="ql-code-block"> private static AtomicLong atomicLong=new AtomicLong(); </div> <div class="ql-code-block"> private static Integer[] arrayOne=new Integer[]{0,1,2,3,4,5,6,7,56,0}; </div> <div class="ql-code-block"> private static Integer[] arrayTwo=new Integer[]{10,1,2,3,4,5,6,0,56,0}; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public static void main(String[] args) throws InterruptedException{ </div> <div class="ql-code-block"> Thread threadOne=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> int size = arrayOne.length; </div> <div class="ql-code-block"> for(int i=0;i&lt;size;i++){ </div> <div class="ql-code-block"> if(arrayOne[i].intValue()==0){ </div> <div class="ql-code-block"> atomicLong.incrementAndGet(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> Thread threadTwo=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> int size = arrayTwo.length; </div> <div class="ql-code-block"> for(int i=0;i&lt;size;i++){ </div> <div class="ql-code-block"> if(arrayTwo[i].intValue()==0){ </div> <div class="ql-code-block"> atomicLong.incrementAndGet(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> threadOne.start(); </div> <div class="ql-code-block"> threadTwo.start(); </div> <div class="ql-code-block"> threadOne.join(); </div> <div class="ql-code-block"> threadTwo.join(); </div> <div class="ql-code-block"> System.out.println("count 0:"+atomicLong.get()); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h2>JDK8新增的原子操作类LongAdder()方法</h2> <h3>简单介绍</h3> <p><br></p> <p>前面提到过了AtomicLong是通过CAS提供的非阻塞的原子性操作,相比阻塞算法的同步器来说性能已经很好了,但是在高并发下大量线程同时去竞争同一个原子变量,由于同时只有一个线程凯跃操作成功,这样就造成了大量线程竞争失败后,会通过无限循环不断进行自选操作尝试CAS,白白浪费了CPU资源</p> <p>使用LongAddr时候会在内部维护多个Cell变量,每个Cell里面有一个初始为零的long变量,在同等的并发量下,争夺单个变量更新操作的线程会减小,变相的减少了争夺共享资源的并发量</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1609216259264610306/5g9uwuae.jpeg"></p> <p>当多个线程在争夺同一个cell变量失败后,并不是在当前的cell变量上一直自旋CAS重试,而是在其他Cell变量上尝试进行CAS,这个改变增加了当前线程重试CAS的成功的可能性,最后在获取LongAdder的值的时候,是把所有的Cell变量的值累加再加上Base返回的</p> <p>LongAdder 维护了一个延迟初始化的原子性更新数组 (默认情况下 Cell 数组是 nu和一个基值变量 base。由于 Cells 占用的内存是相对比较大的,所以一开始并不创建它而是在需要时创建,也就是惰性加载。</p> <p>当一开始判断 Cell 数组是 null 并且并发线程较少时,所有的累加操作都是对 base变量进行的。保持 Cell 数组的大小为2的N次方,在初始化时 Cel 数组中的 Cel元素个数为2,数组里面的变量实体是 Cell 类型。Cell 类型是 AtomicLong 的一个改进,用来减少缓存的争用,也就是解决伪共享问题。</p> <p>对于大多数孤立的多个原子操作进行字节填充是浪费的,因为原子性操作都是无规律地分散在内存中的(也就是说多个原子性变量的内存地址是不连续的),多个原子变量被放入同一个缓存行的可能性很小。但是原子性数组元素的内存地址是连续的,所以数组内的多个元素能经常共享缓存行,因此这里使用 <a href="http://@sun.misc.Contended" target="_blank">@sun.misc.Contended</a> 注解对 Cell类进行字节填充,这防止了数组中多个元素共享一个缓存行,在性能上是一个提升。</p> <p><br></p> <h3>代码分析</h3> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>(1)LongAdder 的结构是怎样的?</li> <li data-list="bullet"><span class="ql-ui"></span>(2)当前线程应该访问 Cell 数组里面的哪一个 Cell 元素?</li> <li data-list="bullet"><span class="ql-ui"></span>(3)如何初始化 Cell 数组?</li> <li data-list="bullet"><span class="ql-ui"></span>(4) Cell 数组如何扩容?</li> <li data-list="bullet"><span class="ql-ui"></span>(5) 线程访问分配的 Cel 元素有冲突后如何处理?</li> <li data-list="bullet"><span class="ql-ui"></span> (6)如何保证线程操作被分配的 Cell 元素的原子性?</li> </ol> <p><br></p> <p>cell的构造</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> @sun.misc.Contended static final class Cell { </div> <div class="ql-code-block"> volatile long value; </div> <div class="ql-code-block"> Cell(long x) { value = x; } </div> <div class="ql-code-block"> final boolean cas(long cmp, long val) { </div> <div class="ql-code-block"> return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // Unsafe mechanics </div> <div class="ql-code-block"> private static final sun.misc.Unsafe UNSAFE; </div> <div class="ql-code-block"> private static final long valueOffset; </div> <div class="ql-code-block"> static { </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> UNSAFE = sun.misc.Unsafe.getUnsafe(); </div> <div class="ql-code-block"> Class&lt;?&gt; ak = Cell.class; </div> <div class="ql-code-block"> valueOffset = UNSAFE.objectFieldOffset </div> <div class="ql-code-block"> (ak.getDeclaredField("value")); </div> <div class="ql-code-block"> } catch (Exception e) { </div> <div class="ql-code-block"> throw new Error(e); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p>可以看到,Cell 的构造很简单,其内部维护一个被声明为 volatile 的变量,这里声为volatile是因为线程操作 value 变量时没有使用锁,为了保证变量的内存可见性这里格声明为volatie的。另外cas 函数通过CAS 操作,保证了当前线程更新时被分配的Cell元素中value值的原子性。另外,Cell 类使用<a href="http://@sun.misc.Contended" target="_blank">@sun.misc.Contended</a> 修饰是为了避免伪共享</p> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>·long sum() 返回当前的值,内部操作是累加所有 Cell内部的 value 值后再累加 bas例如下面的代码,由于计算总和时没有对 Cell 数组进行加锁,所以在累加过程可能有其他线程对Cell 中的值进行了修改,也有可能对数组进行了扩容,所以sum返回的值并不是非常精确的,其返回值并不是一个调用 sum 方法时的原子快照值</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"> public long sum() { </div> <div class="ql-code-block"> Cell[] as = cells; Cell a; </div> <div class="ql-code-block"> long sum = base; </div> <div class="ql-code-block"> if (as != null) { </div> <div class="ql-code-block"> for (int i = 0; i &lt; as.length; ++i) { </div> <div class="ql-code-block"> if ((a = as[i]) != null) </div> <div class="ql-code-block"> sum += a.value; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> return sum; </div> <div class="ql-code-block"> } </div> </div> <ol> <li data-list="bullet"><span class="ql-ui"></span> void reset() 为重置操作,如下代码把 base 置为 0,如果 Cell 数组有元素,则元素值被重置为0。</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"> public void reset() { </div> <div class="ql-code-block"> Cell[] as = cells; Cell a; </div> <div class="ql-code-block"> base = 0L; </div> <div class="ql-code-block"> if (as != null) { </div> <div class="ql-code-block"> for (int i = 0; i &lt; as.length; ++i) { </div> <div class="ql-code-block"> if ((a = as[i]) != null) </div> <div class="ql-code-block"> a.value = 0L; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <ol> <li data-list="bullet"><span class="ql-ui"></span> long sumThenReset0是 sum 的改造版本,如下代码在使用sum累加对应的Cell值后把当前 Cell 的值重置为 0,base 重置为0。这样,当多线程调用该方法时会有问题比如考虑第一个调用线程清空 Cell 的值,则后一个线程调用时累加的都是0值。</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"> public long sumThenReset() { </div> <div class="ql-code-block"> Cell[] as = cells; Cell a; </div> <div class="ql-code-block"> long sum = base; </div> <div class="ql-code-block"> base = 0L; </div> <div class="ql-code-block"> if (as != null) { </div> <div class="ql-code-block"> for (int i = 0; i &lt; as.length; ++i) { </div> <div class="ql-code-block"> if ((a = as[i]) != null) { </div> <div class="ql-code-block"> sum += a.value; </div> <div class="ql-code-block"> a.value = 0L; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> return sum; </div> <div class="ql-code-block"> } </div> </div> <p>long longValue0等价于 sum0)。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>下面主要看下 add 方法的实现,从这个方法里面就可以找到其他问题的答案。</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"> public void add(long x) { </div> <div class="ql-code-block"> Cell[] as; long b, v; int m; Cell a; </div> <div class="ql-code-block"> if ((as = cells) != null || !casBase(b = base, b + x)) { </div> <div class="ql-code-block"> boolean uncontended = true; </div> <div class="ql-code-block"> if (as == null || (m = as.length - 1) &lt; 0 || </div> <div class="ql-code-block"> (a = as[getProbe() &amp; m]) == null || </div> <div class="ql-code-block"> !(uncontended = a.cas(v = a.value, v + x))) </div> <div class="ql-code-block"> longAccumulate(x, null, uncontended); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p></p> <div class="ql-code-block-container"> <div class="ql-code-block"> final boolean casBase(long cmp, long val){ </div> <div class="ql-code-block"> return UNSAFE.compareAndSwapLong(this,BASE,cmp, val); </div> <div class="ql-code-block"> } </div> </div> <p>代码 (1)首先看 cells 是否为null,如果为 null 则当前在基础变量 base 上进行累加,这时候就类似AtomicLong 的操作。</p> <p>如果cells 不为 null 或者线程执行代码(1)的 CAS 操作失败了,则会去执行代码(2),代码(2)(3)决定当前线程应该访问 cells 数组里面的哪一个 Cell 元素,如果当前线程射的元素存在则执行代码 (4),使用CAS 操作去更新分配的 Cell 元素的 value 值,如当前线程映射的元素不存在或者存在但是 CAS 操作失败则执行代码(5)。其实将代码(2(3)(4)合起来看就是获取当前线程应该访问的 cells 数组的 Cell 元素,然后进行 CAS更新操作,只是在获取期间如果有些条件不满足则会跳转到代码(5) 执行。另外当前线程应该访问 cells数组的哪一个Cell元素是通过getProbe0)&amp; m进行计算的,其中m是当cells 数组元素个数 -1,getProbe0 则用于获取当前线程中变量 threadLocalRandomProbe值,这个值一开始为0,在代码(5)里面会对其进行初始化。并且当前线程通过分配的Cell 元素的cas 函数来保证对 Cell 元素 value 值更新的原子性,到这里我们回答了问题2和问题6。</p> <p><br></p> <p>该代码为cells数组初始化和扩容的代码</p> <div class="ql-code-block-container"> <div class="ql-code-block"> final void longAccumulate(long x, LongBinaryOperator fn, </div> <div class="ql-code-block"> boolean wasUncontended) { </div> <div class="ql-code-block"> int h; </div> <div class="ql-code-block"> if ((h = getProbe()) == 0) { </div> <div class="ql-code-block"> ThreadLocalRandom.current(); // force initialization </div> <div class="ql-code-block"> h = getProbe(); </div> <div class="ql-code-block"> wasUncontended = true; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> boolean collide = false; // True if last slot nonempty </div> <div class="ql-code-block"> for (;;) { </div> <div class="ql-code-block"> Cell[] as; Cell a; int n; long v; </div> <div class="ql-code-block"> if ((as = cells) != null &amp;&amp; (n = as.length) &gt; 0) { </div> <div class="ql-code-block"> if ((a = as[(n - 1) &amp; h]) == null) { </div> <div class="ql-code-block"> if (cellsBusy == 0) { // Try to attach new Cell </div> <div class="ql-code-block"> Cell r = new Cell(x); // Optimistically create </div> <div class="ql-code-block"> if (cellsBusy == 0 &amp;&amp; casCellsBusy()) { </div> <div class="ql-code-block"> boolean created = false; </div> <div class="ql-code-block"> try { // Recheck under lock </div> <div class="ql-code-block"> Cell[] rs; int m, j; </div> <div class="ql-code-block"> if ((rs = cells) != null &amp;&amp; </div> <div class="ql-code-block"> (m = rs.length) &gt; 0 &amp;&amp; </div> <div class="ql-code-block"> rs[j = (m - 1) &amp; h] == null) { </div> <div class="ql-code-block"> rs[j] = r; </div> <div class="ql-code-block"> created = true; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } finally { </div> <div class="ql-code-block"> cellsBusy = 0; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> if (created) </div> <div class="ql-code-block"> break; </div> <div class="ql-code-block"> continue; // Slot is now non-empty </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> collide = false; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> else if (!wasUncontended) // CAS already known to fail </div> <div class="ql-code-block"> wasUncontended = true; // Continue after rehash </div> <div class="ql-code-block"> else if (a.cas(v = a.value, ((fn == null) ? v + x : </div> <div class="ql-code-block"> fn.applyAsLong(v, x)))) </div> <div class="ql-code-block"> break; </div> <div class="ql-code-block"> else if (n &gt;= NCPU || cells != as) </div> <div class="ql-code-block"> collide = false; // At max size or stale </div> <div class="ql-code-block"> else if (!collide) </div> <div class="ql-code-block"> collide = true; </div> <div class="ql-code-block"> else if (cellsBusy == 0 &amp;&amp; casCellsBusy()) { </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> if (cells == as) { // Expand table unless stale </div> <div class="ql-code-block"> Cell[] rs = new Cell[n &lt;&lt; 1]; </div> <div class="ql-code-block"> for (int i = 0; i &lt; n; ++i) </div> <div class="ql-code-block"> rs[i] = as[i]; </div> <div class="ql-code-block"> cells = rs; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } finally { </div> <div class="ql-code-block"> cellsBusy = 0; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> collide = false; </div> <div class="ql-code-block"> continue; // Retry with expanded table </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> h = advanceProbe(h); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> else if (cellsBusy == 0 &amp;&amp; cells == as &amp;&amp; casCellsBusy()) { </div> <div class="ql-code-block"> boolean init = false; </div> <div class="ql-code-block"> try { // Initialize table </div> <div class="ql-code-block"> if (cells == as) { </div> <div class="ql-code-block"> Cell[] rs = new Cell[2]; </div> <div class="ql-code-block"> rs[h &amp; 1] = new Cell(x); </div> <div class="ql-code-block"> cells = rs; </div> <div class="ql-code-block"> init = true; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } finally { </div> <div class="ql-code-block"> cellsBusy = 0; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> if (init) </div> <div class="ql-code-block"> break; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> else if (casBase(v = base, ((fn == null) ? v + x : </div> <div class="ql-code-block"> fn.applyAsLong(v, x)))) </div> <div class="ql-code-block"> break; // Fall back on using base </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p>在被初始化或者扩容,或者当前在创建新的 Cell 元素、通过 CAS 操作来进行0或 1状态的切换,这里使用 casCellsBusy 函数。假设当前线程通过 CAS 设置 ellsBusy 为1,则前线程开始初始化操作,那么这时候其他线程就不能进行扩容了。如代码 《14.1)初始化cells 数组元素个数为 2,然后使用 h&amp;1 计算当前线程应该访 el 数组的哪个位置,也就是使用当前线程的 threadLocalRandomProbe 变量值&amp; (cells 数组元素个数 -1),然后标示cels 数组已经被初始化,最后代码 (14.3) 重置了 ellsBusy 标记。显然这里没有使用CAS 操作,却是线程安全的,原因是 cellsBusy 是 volatile 类型的,这保证了变量的内存可见性,另外此时其他地方的代码没有机会修改 cellsBusy 的值。在这里初始化的 cells 数组里面的两个元素的值目前还是 null。这里回答了问题 3,知道了 cells 数组如何被初始化。</p> <p>cells数组的扩容是在代码(12)中进行的,对 cells 扩容是有条件的,也就是代码(10)(11)的条件都不满足的时候。具体就是当前 cells 的元素个数小于当前机器 CPU 个数并目当前多个线程访问了 cells 中同一个元素,从而导致冲突使其中一个线程 CAS 失败时才会进行扩容操作。这里为何要涉及 CPU 个数呢?其实在基础篇中已经讲过,只有当每个 CPU都运行一个线程时才会使多线程的效果最佳,也就是当 cells 数组元素个数与 CPU个数一致时,每个 Cell 都使用一个 CPU 进行处理,这时性能才是最佳的。代码(12)中的扩容操作也是先通过 CAS 设置 cellsBusy 为1,然后才能进行扩容。假设 CAS 成功则执行代码(12.1)将容量扩充为之前的 2 倍,并复制 Cell 元素到扩容后数组。另外,扩容后 cells 数组里面除了包含复制过来的元素外,还包含其他新元素,这些元素的值目前还是 null。这里回答了问题4。</p> <p>在代码(7)(8)中,当前线程调用add 方法并根据当前线程的随机数threadLocalRandomProbe 和 cells 元素个数计算要访问的 Cell元素下标,然后如果发现对应下标元素的值为 null,则新增一个 Cell 元素到 cells 数组,并且在将其添加到 cells 数组之前要竞争设置 cellsBusy 为 1。</p> <p>代码(13)对CAS失败的线程重新计算当前线程的随机值 threadLocalRandomProbe,以减少下次访问 cells 元素时的冲突机会。这里回答了问题 5。</p> <p><br></p> <h3>小结</h3> <p><br></p> <p>介绍了新增的JDK8中新增的LongAdder原子操作类,该类通过内部cells数组分担了高并发下多线程同时对一个原子变量进行更新时的竞争量,让多个线程可以同时对cells数组的元素进行操作,数组元素cell使用</p> <p><a href="http://@sun.misc.Contended" target="_blank">@sun.misc.Contended</a>注解进行修饰,这避免了cells数组内多个原子变量被放入同一个缓存行,也就避免了伪共享</p> <p><br></p> <h2>LongAccumulator类原理探究</h2> <p><br></p> <p>LongAdder 类是 LongAccumulator 的一个特例,LongAccumulator 比 LongAdder 的更强大。</p> <p>其中accumulatorFunction 是一个双目运算器接口,其根输入的两个参数返回一个计算值,identity 则是 LongAccumulator 累加器的初始值</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public LongAccumulator(LongBinaryOperator accumulatorFunction, </div> <div class="ql-code-block"> long identity) { </div> <div class="ql-code-block"> this.function = accumulatorFunction; </div> <div class="ql-code-block"> base = this.identity = identity; </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p>调用 LongAdder 就相于使用下面的方式调用 LongAccumulator:</p> <p><br></p> <p>调用 LongAdder 就相于使用下面的方式调用 LongAccumulator:</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public interface LongBinaryOperator{ </div> <div class="ql-code-block"> long applyAsLong(long left,long right); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> LongAdder adder new LongAdder(); </div> <div class="ql-code-block"> LongAccumulator accumulator =new LongAccumulator(new LongBinaryOperator(){ </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public long applyhsLong(long left, long right) { </div> <div class="ql-code-block"> return left + right; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> },0); </div> </div> <p>LongAccumulator 相比于 LongAdder,可以为累加器提供非 0的初始值,后者只能提供默认的0值。另外,前者还可以指定累加规则,比如不进行累加而进行相乘,只需要在构造LongAccumulator时传入自定义的双目运算器即可,后者则内置累加的规则。</p> <p>从下面代码我们可以知道,LongAccumulator 相比于 LongAdder 的不同在于,在调用caseBase时,后者传递的是b+x,前者使用了r=function.ApplyAsLong(b=base,x)来计算</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public void add(long x) { </div> <div class="ql-code-block"> Cell[] as; long b, v; int m; Cell a; </div> <div class="ql-code-block"> if ((as = cells) != null || !casBase(b = base, b + x)) { </div> <div class="ql-code-block"> boolean uncontended = true; </div> <div class="ql-code-block"> if (as == null || (m = as.length - 1) &lt; 0 || </div> <div class="ql-code-block"> (a = as[getProbe() &amp; m]) == null || </div> <div class="ql-code-block"> !(uncontended = a.cas(v = a.value, v + x))) </div> <div class="ql-code-block"> longAccumulate(x, null, uncontended); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> public void accumulate(long x) { </div> <div class="ql-code-block"> Cell[] as; long b, v, r; int m; Cell a; </div> <div class="ql-code-block"> if ((as = cells) != null || </div> <div class="ql-code-block"> (r = function.applyAsLong(b = base, x)) != b &amp;&amp; !casBase(b, r)) { </div> <div class="ql-code-block"> boolean uncontended = true; </div> <div class="ql-code-block"> if (as == null || (m = as.length - 1) &lt; 0 || </div> <div class="ql-code-block"> (a = as[getProbe() &amp; m]) == null || </div> <div class="ql-code-block"> !(uncontended = </div> <div class="ql-code-block"> (r = function.applyAsLong(v = a.value, x)) == v || </div> <div class="ql-code-block"> a.cas(v, r))) </div> <div class="ql-code-block"> longAccumulate(x, function, uncontended); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p>前者调用的时候传递的是function,后者是null</p> <p>当fn为null时候就使用v+x加法运算,这时候等价于LongAdder,当fn不为null时候则使用传递fn函数计算</p> <div class="ql-code-block-container"> <div class="ql-code-block"> else if (caseBase(v = base, ((fn = null) ? v + x : fn.applyAsLong(v, x)))) </div> <div class="ql-code-block"> break; </div> <div class="ql-code-block"> } </div> </div> <p>总结:本节简单介绍了LongAccumluator的原理,LongAdder是LongAccumluator的一个特例,只是后者提供更加强大的功能,可以让用户自定义累加规则</p> <p><br></p> <h2>总结</h2> <p><br></p> <p>本章介绍了并发包中的原子性操作类,这些类都是使用非阻塞算法 CAS 实现的,这相比使用锁实现原子性操作在性能上有很大提高。首先讲解了最简单的AtomicLong 类的实现原理,然后讲解了JDK 8中新增的 LongAdder 类和 LongAccumulator 类的原理。学习完本章后,希望读者在实际项目环境中能因地制宜地使用原子性操作类来提升系统性能。</p> <p><br></p> <p><br></p> </div> </body> </html>

《java并发编程之美》读书笔记

<html> <head></head> <body> <div class="content ql-editor"> <h1>阅读</h1> <p>半个月前就收到了第一次中奖的书,因为读的比较慢,所以先把读过的笔记发出来。</p> <h3><strong style="color: rgb(39, 56, 73);"><br></strong></h3> <h3><strong style="color: rgb(39, 56, 73);"><br></strong></h3> <h3><strong style="color: rgb(39, 56, 73);">1.1 什么是线程</strong></h3> <p><br></p> <p>线程是进程中的一个实体,线程本身是不会独立存在的。进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程的一个执行路径,一个进程中至少有一个线程,进程中的多个线程共享进程的资源</p> <p>操作系统在分配资源时是把资源分配给进程的,但是 CPU 资源比较特殊,它是被分配到线程的,因为要真正占用 CPU 运行的是线程,所以也说线程是 CPU 分配的基本单位</p> <p>多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器和栈区域</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>程序计数器是一块内存区域,用来记录线程当前要执行的指令地址</li> <li data-list="bullet"><span class="ql-ui"></span>需要注意的是,如果执行的是 native 方法,那么 pc 计数器记录的是 undefined 地址,只有执行的是 Java 代码时 pc 计数器记录的才是下一条指令的地址</li> <li data-list="bullet"><span class="ql-ui"></span>每个线程都有自己的栈资源,用于存储该线程的局部变量,这些局部变量是该线程私有的,其他线程是访问不了的,除此之外栈还用来存放线程的调用栈帧</li> <li data-list="bullet"><span class="ql-ui"></span>堆是一个进程中最大的一块内存,堆是被进程中的所有线程共享的,是进程创建时分配的,堆里面主要存放使用 new 操作创建的对象实例</li> <li data-list="bullet"><span class="ql-ui"></span>方法区则用来存放 JVM 加载的类、常量及静态变量等信息,也是线程共享的</li> </ol> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">1.2 线程三种创建方式的优缺点</strong></h3> <p><br></p> <p>Java 中有三种线程创建方式,分别为实现 Runnable 接口的 run 方法,继承 Thread 类并重写 run 方法,以及使用 FutureTask 方式</p> <p>使用继承方式的好处是方便传参,可以在子类里添加成员变量,通过 set 方法设置参数或者通过构造函数进行传递,而如果使用 Runnable 方式,则只能使用主线程里面被声明为 final 的变量。不好的地方是 Java 不支持多继承,而如果继承了 Thread 类,那么子类不能再继承其他类,而 Runnable 则没有这个限制。前两种方式都没办法拿到任务执行的返回结果,但是 FutureTask 方式可以。</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">1.3 线程通知与等待</strong></h3> <p><br></p> <p>Java 中的 Object 类是所有类的父类,鉴于继承机制,Java 把所有类都需要的方法放到了 Object 类里面,其中就包含通知与等待系列的函数</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">wait() / wait(long timeout)</strong></h3> <p><br></p> <p>当一个线程调用一个共享变量的 wait() 方法时,该调用线程会被阻塞挂起,直到发生下面几件事情之一才返回:</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>其他线程调用了该共享对象的 notify() 或者 notifyAll() 方法</li> <li data-list="ordered"><span class="ql-ui"></span>其他线程调用了该线程的 interrupt() 方法,该线程抛出 InterruptedException 异常返回</li> <li data-list="ordered"><span class="ql-ui"></span>如果带有超时参数,没有在指定时间的 timeout ms 时间内被其他线程调用该共享变量的 notify() 或者 notifyAll() 方法唤醒,那么该函数还是会因为超时而返回</li> <li data-list="ordered"><span class="ql-ui"></span>不加参数的 wait() 方法内部就是调用了 wait(0)</li> </ol> <p>当线程调用共享对象的 wait() 方法时,当前线程只会释放当前共享对象的锁,当前线程持有的其他共享对象的监视器锁并不会被释放</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">虚假唤醒</strong></h3> <p><br></p> <p>一个线程可以从挂起状态变为可以运行状态(也就是被唤醒),即使该线程没有被其他线程调用 notify()、notifyAll() 方法进行通知,或者被中断,或者等待超时,这就是所谓的 <strong>虚假唤醒</strong> 。</p> <p>虚假唤醒在应用实践中很少发生,但要防患于未然,做法就是不停的测试该线程被唤醒的条件是否满足,不满足则继续等待,也就是说在一个循环中调用 wait() 方法进行防范。退出循环的条件是满足了唤醒该线程的条件。</p> <div class="ql-code-block-container"> <div class="ql-code-block"> &nbsp;synchronized (obj) { </div> <div class="ql-code-block"> &nbsp; &nbsp; &nbsp;while (条件不满足) { </div> <div class="ql-code-block"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;obj.wait(); </div> <div class="ql-code-block"> &nbsp; &nbsp; } </div> <div class="ql-code-block"> &nbsp;} </div> </div> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">notify()</strong></h3> <p><br></p> <p>一个线程调用共享对象的 notify() 方法后,会唤醒一个在该共享变量上调用 wait 系列方法后被挂起的线程。一个共享变量上可能会有多个线程在等待,具体唤醒哪个等待的线程是随机的。这个被唤醒的线程还需要和其他线程一起竞争该锁,只有该线程竞争到了共享变量的监视器锁后才可以继续执行</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">notifyAll()</strong></h3> <p><br></p> <p>notifyAll() 方法会唤醒所有在该共享变量上由于调用 wait 系列方法而被挂起的线程</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">1.4 等待线程执行终止的 join 方法</strong></h3> <p><br></p> <p>Thread 类中的 join 方法可以用来等待多个线程全部加载完毕再汇总处理</p> <p>线程 A 调用线程 B 的 join 方法后会被阻塞,当其他线程调用了线程 A 的 interrupt() 方法中断了线程 A 时,线程 A 会抛出 InterruptedException 异常而返回</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">1.5 让线程睡眠的 sleep 方法</strong></h3> <p><br></p> <p>Thread 类有一个静态的 sleep() 方法,当一个执行中的线程调用了 Thread 的 sleep() 方法后,调用线程会暂时让出指定的执行权,也就是在这期间不参与 CPU 的调度,但是该线程所拥有的监视器资源,比如锁还是持有不让出的。指定的睡眠时间到了后该函数会正常返回,线程就处于就绪状态,然后参与 CPU 的调度,获取到 CPU 的资源后就可以运行了。</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">1.6 让出 CPU 执行权的 yield 方法</strong></h3> <p><br></p> <p>当一个线程调用了 Thread 类的静态方法 yield() 时,是在告诉线程调度器自己占有的时间片中还没有使用完的部分自己不想使用了,这暗示线程调度器现在就可以进行下一轮的线程调度</p> <p>sleep() 和 yield() 方法的区别在于,当线程调用 sleep() 方法时调用线程会被阻塞挂起指定的时间,在这期间线程调度器不会去调度该线程。而调用 yield() 方法时,线程只是让出自己剩余的时间片,并没有被阻塞挂起,而是处于就绪状态,线程调度器下一次调度时就有可能调度到当前线程执行。</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">1.7 线程中断</strong></h3> <p><br></p> <p>Java 中的线程中断是一种线程间的协作模式,通过设置线程的中断标志并不能直接终止该线程的执行,而是被中断的线程根据中断状态自行处理</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>void interrupt() : 中断线程,当线程 A 运行时,线程 B 可以调用线程 A 的 interrupt() 方法来设置线程 A 的中断标志为 true 并立即返回。设置标志仅仅是设置标志,线程 A 实际并没有被中断,它会继续往下执行。</li> <li data-list="bullet"><span class="ql-ui"></span>boolean isInterrupted() : 检测当前线程是否被中断,如果是返回 true,否则返回 false</li> <li data-list="bullet"><span class="ql-ui"></span>boolean interrupted() : 检测当前线程是否被中断,如果是返回 true,否则返回 false,如果该方法发现当前线程被中断,则会清除中断标志,并且该方法是 static 方法,可以通过 Thread 类直接调用。</li> </ol> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">1.8 线程上下文切换</strong></h3> <p><br></p> <p>线程上下文切换时机有:当前线程的 CPU 时间片使用完处于就绪状态时,当前线程被其他线程中断时</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">1.9 线程死锁</strong></h3> <h3><strong style="color: rgb(39, 56, 73);">什么是死锁</strong></h3> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>死锁是指两个或两个以上的线程在执行过程,因争夺资源而造成的互相等待的现象,在无外力作用的情况下,这些线程会一直等待而无法继续运行下去</li> </ol> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">产生死锁的条件</strong></h3> <p><br></p> <p>死锁的产生必须具备以下四个条件:</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>互斥条件:指线程对已经获取到的资源进行排它性使用,即该资源同时只由一个线程占用。如果此时还有其他线程请求使用该资源,则请求者只能等待,直至占有资源的线程释放该资源</li> <li data-list="bullet"><span class="ql-ui"></span>请求并持有条件:指一个线程已经持有了至少一个资源,但又提出了新的资源请求,而新资源已被其他线程占有,所以当前线程会被阻塞,但阻塞的同时并不释放自己已经获取的资源</li> <li data-list="bullet"><span class="ql-ui"></span>不可剥夺条件:指线程获取到的资源在自己使用完之前不能被其他线程抢占,只有在自己使用完毕后才由自己释放该资源</li> <li data-list="bullet"><span class="ql-ui"></span>环路等待条件:指在发生死锁时,必然存在一个线程一资源的环形链,即线程集合 {T0, T1, T2, ... , Tn} 中的 T0 正在等待一个 T1 占用的资源,T1 正在等待 T2 占用的资源,......Tn 正在等待已被 T0 占用的资源。</li> </ol> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">如何避免线程死锁</strong></h3> <p><br></p> <p>要想避免死锁,只需要破坏掉至少一个构造死锁的必要条件即可,但是目前只有 <strong>请求并持有</strong> 和 <strong>环路等待</strong> 条件是可以被破坏的</p> <p>资源的有序分配会避免死锁,因为资源的有序性破坏了资源的请求并持有条件和环路等待条件,因此避免了死锁。</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">1.10 守护线程与用户线程</strong></h3> <p><br></p> <p>Java 中的线程分为两类,分别为 daemon 线程(守护线程)和 user 线程(用户线程)。在 JVM 启动时会调用 main 函数,main 函数所在的线程就是一个用户线程,而垃圾回收线程则是守护线程</p> <p>守护线程和用户线程区别之一是当最后一个非守护线程结束时,JVM 会正常退出,而不管当前是否有守护线程,也就是说守护线程是否结束并不影响 JVM 的退出。言外之意,只要有一个用户线程还没结束,正常情况下 JVM 就不会退出</p> <p>创建守护线程的的方式是,设置线程的 daemon 参数为 true 即可</p> <p>总的来说,如果希望在主线程结束后 JVM 进程马上结束,那么在创建线程时可以将其设置为守护线程,如果希望在主线程结束后子线程继续工作,等子线程结束后再让 JVM 进程结束,那么就将子线程设置为用户线程</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">1.11 ThreadLocal</strong></h3> <p><br></p> <p>ThreadLocal 是 JDK 包提供的,它提供了线程本地变量,也就是如果你创建了一个 ThreadLocal 变量,那么访问这个变量的每个线程都会有这个变量的一个本地副本。当多个线程操作这个变量时,实际操作的是自己本地内存里面的变量,从而避免了线程安全问题。</p> <p>ThreadLocal 是一个 HashMap 结构,其中 key 就是当前 ThreadLocal 的实例引用,value 是通过 set 方法传递的值。ThreadLocal 变量在父线程中被设置值后,在子线程中是获取不到的。</p> <p><br></p> <h2><strong style="color: rgb(39, 56, 73);">2. 并发编程的其他基础知识</strong></h2> <h3><strong style="color: rgb(39, 56, 73);">2.1 为什么要进行多线程并发编程</strong></h3> <p><br></p> <p>多核 CPU 时代的到来打破了单核 CPU 对多线程效能的限制。多个 CPU 意味着每个线程可以使用自己的 CPU 运行,这减少了线程上下文切换的开销,但随着对应用系统性能和吞吐量要求的提高,出现了处理海量数据和请求的要求,这些都会高并发编程有着迫切的需求。</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">2.2 Java 中的线程安全问题</strong></h3> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>共享资源:就是说该资源被多个线程所持有或者说多个线程都可以去访问该资源</li> </ol> <p>线程安全问题是指当多个线程同时读写一个共享资源并且没有任何同步措施时,导致出现脏数据或者其他不可预见的结果的问题</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">2.3 Java 中共享变量的内存可见性问题</strong></h3> <p><br></p> <p>当一个线程操作共享变量时,它首先从主内存复制共享变量到自己的工作内存,然后对工作内存里的变量进行处理,处理完后将变量值更新到主内存</p> <p>假如线程 A 和线程 B 使用不同的 CPU 执行,此时由于 Cache 的存在,将会导致内存不可见问题</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">2.4 synchronized</strong></h3> <h3><strong style="color: rgb(39, 56, 73);">2.4.1 synchronized 关键字介绍</strong></h3> <p><br></p> <p>synchronized 块是 Java 提供的一种原子性内置锁,Java 中的每个对象都可以把它当做一个同步锁来使用,这些 Java 内置的使用者看不到的锁被称为 <strong>内部锁</strong>,也叫做 <strong>监视器锁</strong> 。</p> <p>内置锁是排它锁,也就是当一个线程获取这个锁后,其他线程必须等待该线程释放锁后才能获取该锁。</p> <p>另外,由于 Java 中的线程是与操作系统中的原生线程一一对应的,所以当阻塞一个线程时,需要从用户态切换到内核态执行阻塞操作,这是很耗时的操作,而 synchronized 的使用就会导致上下文切换。</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">2.4.2 synchronized 的内存语义</strong></h3> <p><br></p> <p>进入 synchronized 块的内存语义是把在 synchronized 块内使用到的变量从线程的工作内存中清除,这样在 synchronized 块内使用到该变量时就不会从线程的工作内存中获取,而是直接从主内存中获取。退出 synchronized 块的内存语义是把在 synchronized 块内对共享变量的修改刷新到主内存。</p> <p>除了可以解决共享变量内存可见性问题外,synchronized 经常被用来实现原子性操作。另外请注意,synchronized 关键字会引起线程上下文切换并带来线程调度开销。</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">2.5 volatile</strong></h3> <p><br></p> <p>对于解决内存可见性的问题,Java 还提供了一种弱形式的同步,也就是使用 volatile 关键字。该关键字可以确保对一个变量的更新对其他线程马上可见。当一个变量被声明为 volatile 时,线程在写入变量时不会把值缓存在寄存器或者其他地方,而是把值刷新回主内存。当其他线程读取该共享变量时,会从主内存重新获取最新值,而不是使用当前线程的工作内存中的值。</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">2.6 Java 中的原子性操作</strong></h3> <p><br></p> <p>所谓原子性操作,是指执行一系列操作时,这些操作要么全部执行,要么全部不执行,不存在只执行其中一部分的情况。</p> <p>线程安全性:即内存可见性和原子性</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">2.7 Java 中的 CAS 操作</strong></h3> <p><br></p> <p>CAS 即 Compare and Swap,是 JDK 提供的非阻塞原子性操作,它通过硬件保证了比较 -- 更新操作的原子性。JDK 里面的 Unsafe 类提供了一系列的 compareAndSwap 方法。</p> <div class="ql-code-block-container"> <div class="ql-code-block"> &nbsp;// 比如说下面这个 </div> <div class="ql-code-block"> &nbsp;boolean compareAndSwapLong(Object obj, long valueOffset, long expect, long update); </div> </div> <p>其中 compareAndSwap 的意思是比较并交换。</p> <p>CAS 有四个操作数,分别为:对象内存位置、对象中的变量的偏移量、变量预期值和新的值。其操作含义是,如果对象 obj 中内存偏移量为 valueOffset 的变量值为 expect ,则使用新的值 update 替换旧的值 expect。这是处理器提供的一个原子性指令。</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">ABA 问题</strong></h3> <p><br></p> <p>CAS 操作有个经典的 ABA 问题。</p> <p>ABA 问题的产生是因为变量的状态值产生了环形转换,就是变量的值可以从 A 到 B,然后再从 B 到 A 。如果变量的值只能朝着一个方向转换,比如 A 到 B,B 到 C,不构成环形,就不会存在问题。JDK 中的 AtomicStampedReference 类给每个变量的状态值都配备了一个时间戳,从而避免了 ABA 问题的产生。</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">2.8 Unsafe 类</strong></h3> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>JDK 的 rt.jar 包中的 Unsafe 类提供了硬件级别的原子性操作,Unsafe 类中的方法都是 native 方法,它们使用 JNI 的方式访问本地 C++ 实现库</li> </ol> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">2.9 Java 指令重排序</strong></h3> <p><br></p> <p>Java 内存模型允许编译器和处理器对指令重排序以提高运行性能,并且只会对不存在数据依赖性的指令重排序。在单线程下重排序可以保证最终执行的结果与程序顺序执行的结果一致,但是在多线程下就会存在问题。</p> <p>重排序在多线程下会导致非预期的程序执行结果,而使用 volatile 修饰变量就可以避免重排序和内存可见性问题。</p> <p>写 volatile 变量时,可以确保 volatile 写之前的操作不会被编译器重排序到 volatile 写之后。读 volatile 变量时,可以确保 volatile 读之后的操作不会被编译器重排序到 volatile 读之前。</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">2.10 伪共享</strong></h3> <h3><strong style="color: rgb(39, 56, 73);">2.10.1 什么是伪共享</strong></h3> <p><br></p> <p>为了解决主内存与 CPU 之间运行速度差的问题,会在 CPU 与主内存之间添加一级或多级高速缓冲器(Cache)。这个 Cache 一般是被集成到 CPU 内部的,所以也叫 CPU Cache 。</p> <p>在 Cache 内部是按行存储的,其中每一行称为一个 Cache 行。Cache 行是 Cache 与主内存进行数据交换的单位。</p> <p>由于存放到 Cache 行的是内存块而不是单个变量,所以可能会把多个变量存放到一个 Cache 行中。当多个线程同时修改一个缓存行里面的多个变量时,由于同时只能有一个线程操作缓存行,所以相比将每一个变量放到一个缓存行,性能会有所下降,这就是伪共享。</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">2.10.2 如何避免伪共享</strong></h3> <p><br></p> <p>在 JDK 8 之前一般都是通过字节填充的方式来避免该问题,也就是创建一个变量时使用填充字段填充该变量所在的缓存行,这就避免了将多个变量存放在同一个缓存行中。</p> <p>JDK 8 提供了一个 sun.misc.Contented 注解,用来解决伪共享问题。在默认情况下,@Contented 注解只用于 Java 核心类,比如 rt 包下的类。如果用户类路径下的类需要使用这个注解,则需要添加 JVM 参数:-XX:-RestrictContented 。</p> <p>总结来说,在多线程下访问同一个缓存行的多个变量时才会出现伪共享,在单线程下访问一个缓存行里面的多个变量反而会对程序运行起到加速作用</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">2.11 锁的概述</strong></h3> <h3><strong style="color: rgb(39, 56, 73);">2.11.1 乐观锁与悲观锁</strong></h3> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>悲观锁是指对数据被外界修改持保守态度,认为数据很容易就会被其他线程修改,所以在数据被处理前先对数据进行加锁,并在整个数据处理过程中,使数据处于锁定状态</li> <li data-list="bullet"><span class="ql-ui"></span>乐观锁是相对悲观锁来说的,它认为数据在一般情况下不会造成冲突,所以在访问记录前不会加排它锁,而是在进行数据提交更新时,才会对数据冲突与否进行检测</li> </ol> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">2.11.2 公平锁与非公平锁</strong></h3> <p><br></p> <p>根据线程获取锁的抢占机制,锁可以分为 <strong>公平锁</strong> 和 <strong>非公平锁</strong></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>公平锁表示线程获取锁的顺序是按照线程请求锁的时间早晚来决定的,也就是最早请求锁的线程将最早获取到锁。</li> <li data-list="bullet"><span class="ql-ui"></span>非公平锁则在运行时闯入,也就是先来不一定先得。</li> </ol> <p>ReentrantLock 提供了公平和非公平锁的实现</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>公平锁:ReentrantLock pairLock = new ReentrantLock(true)</li> <li data-list="bullet"><span class="ql-ui"></span>非公平锁:ReentrantLock pairLock = new ReentrantLock(false) ,默认是非公平锁</li> </ol> <p>在没有公平性需求的前提下尽量使用非公平锁,因为公平锁会带来性能开销</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">2.11.3 独占锁与共享锁</strong></h3> <p><br></p> <p>根据锁只能被单个线程持有还是能被多个线程共同持有,锁可以分为 <strong>独占锁</strong> 和 <strong>共享锁</strong> 。</p> <p>独占锁保证任何时候都只有一个线程能得到锁,ReentrantLock 就是以独占方式实现的。共享锁则可以同时由多个线程持有,例如 ReadWriteLock 读写锁,它允许一个资源可以被多个线程同时进行读操作。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>独占锁是一种悲观锁,由于每次访问资源都先加上互斥锁,这限制了并发性,因为读操作并不会影响数据的一致性,而独占锁只允许在同一时间由一个线程读取数据,其他线程必须等待当前线程释放锁才能进行读取</li> <li data-list="bullet"><span class="ql-ui"></span>共享锁则是一种乐观锁,它放宽了加锁的条件,允许多个线程同时进行读操作</li> </ol> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">2.11.4 可重入锁</strong></h3> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>当一个线程要获取一个被其他线程持有的独占锁时,该线程会被阻塞,那么当一个线程再次获取它自己已经获取的锁时,如果不被阻塞,那么该锁就是可重入的。</li> </ol> <p>synchronized 内部锁是可重入锁。</p> <p>可重入锁的原理是在锁内部维护了一个线程标示,用来标示该锁目前被哪个线程占用,然后关联一个计数器,当计数器值为 0 时说明该锁没有被任何线程占用,当一个线程获取了该锁,计数器值会变为 1,这时其他线程再来获取锁时会发现锁的所有者不是自己而被阻塞挂起。但是当获取了该锁的线程再次获取锁时发现锁拥有者是自己,计数器值就 + 1,当释放锁后,计数器值 - 1。当计数器值为 0 时,锁里面的线程标示被重置为 null ,这时候被阻塞的线程会被唤醒来竞争获取该锁。</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">2.11.5 自旋锁</strong></h3> <p><br></p> <p>由于 Java 中的线程是与操作系统中的线程一一对应的,所以当一个线程在获取锁失败后,会被切换到用户态而被挂起。当该线程获取到锁时又需要将其切换到内核状态而唤醒该线程。而从用户状态切换到内核状态的开销是比较大的,在一定程度上会影响并发性能。</p> <p>自旋锁则是,当前线程在获取锁时,如果发现锁已经被其他线程占有,它不马上阻塞自己,在不放弃 CPU 使用权的情况下,多次尝试获取(默认次数是 10,可以使用 -XX:PreBlockSpinsh 参数设置该值),很有可能在后面几次尝试中其他线程已经释放了锁。如果尝试指定的次数后仍没有获取到锁则当前线程才会被阻塞挂起。</p> <p>由此看来自旋锁是使用 CPU 时间换取线程阻塞与调度的开销,但是很有可能这些 CPU 时间白白浪费了。</p> <p><br></p> <h2><strong style="color: rgb(39, 56, 73);">3. ThreadLocalRandom</strong></h2> <h3><strong style="color: rgb(39, 56, 73);">3.1 Random 类及其局限性</strong></h3> <p><br></p> <p>每个 Random 实例里面都有一个原子性的种子变量用来记录当前的种子值,当要生成新的随机数时需要根据当前种子计算新的种子并更新会原子变量。当多线程下使用单个 Random 实例生成随机数时,当多个线程同时计算随机数来计算新的种子时,多个线程会竞争同一个原子变量的更新操作,由于原子变量的更新是 CAS 操作,同时只有一个线程会成功,所以会造成大量线程进行自旋重试,这会降低并发性能,所以 ThreadLocalRandom 应运而生。</p> <p><br></p> <h3><strong style="color: rgb(39, 56, 73);">3.2 ThreadLocalRandom</strong></h3> <p><br></p> <p>每个线程都维护一个种子变量,则每个线程生成随机数时都根据自己老的种子计算新的种子,并使用新种子更新老的种子,再根据新种子计算随机数,就不会存在竞争问题了,这会大大提高并发性能。</p> <p>ThreadLocalRandom 使用 ThreadLocal 的原理,让每个线程都持有一个本地的种子变量,该种子变量只有在使用随机数时才会被初始化。在多线程下计算新种子时是根据自己线程内维护的种子变量进行更新,从而避免了竞争。</p> <p><br></p> </div> </body> </html>

java并发编程之美读书笔记前两章

<html> <head></head> <body> <div class="content ql-editor"> <p><br></p> <p><br></p> <p><br></p> <p>1--80面</p> <p><br></p> <p><br></p> <h1>java并发编程基础</h1> <h2>什么是线程</h2> <h3><strong>进程:</strong></h3> <p><br></p> <p>是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位</p> <p><br></p> <h3><strong>线程:</strong></h3> <p><br></p> <p>是进程的一次执行路径,一个进程至少有一个线程,进程中的多个线程共享进程的资源.</p> <p>线程是CPU分配的基本单位</p> <p><br></p> <h3><strong>栈:</strong></h3> <p><br></p> <p>每个线程都有自己的栈资源,用于存储该线程的局部变量,这些局部变量是该线程私有的,其他线程无法访问,除此之外栈还可以用来存放线程的调用栈帧.</p> <p><br></p> <h3><strong>堆:</strong></h3> <p><br></p> <p>堆是一个进程中最大的一个内存,堆是被进程中的所有线程共享的,是进程创建时分配的,堆里面主要存放使用new操作创建的对象实例.</p> <p><br></p> <h3><strong>方法区:</strong></h3> <p><br></p> <p>存放JVM加载的类,常量及静态变量等信息,也是线程共享的.</p> <p><br></p> <h2>线程的创建与运行</h2> <p><br></p> <p><br></p> <p>java一共三种创建线程的方式</p> <ol> <li data-list="bullet" class="ql-indent-1"><span class="ql-ui"></span>实现Runnable接口的run方法</li> <li data-list="bullet" class="ql-indent-1"><span class="ql-ui"></span>继承Thread类并重写run方法</li> <li data-list="bullet" class="ql-indent-1"><span class="ql-ui"></span>使用FutureTask方式</li> </ol> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * @author xingchen </div> <div class="ql-code-block"> * @version V1.0 </div> <div class="ql-code-block"> * @Package com.并发.线程的创建 </div> <div class="ql-code-block"> * @date 2023/4/2 15:53 </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * 1.使用继承的好处是方便传参,可以在子类里面添加成员方法 </div> <div class="ql-code-block"> * 通过set或者构造函数进行参数传递 </div> <div class="ql-code-block"> * 2.使用Runnable则只能使用主线程里面被声明的final变量.不好的地方是java不支持多继承 </div> <div class="ql-code-block"> * 3.继承Thread那么子类不能再继承其他类 而Runnable没有这个限制 </div> <div class="ql-code-block"> * 4.前两个方法都不支持返回参数,最后的Futuretask方式可以 </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> public class ThreadTest { </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 第一种实现多线程的方式 </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * 优点: </div> <div class="ql-code-block"> * 在run()方法内获取当前线程不需要使用Thread.currentThread()方法,直接使用this就可以获取当前线程 </div> <div class="ql-code-block"> * 缺点: </div> <div class="ql-code-block"> * java不支持多继承,如果继承Thread就不能继承其他父类 </div> <div class="ql-code-block"> * 任务和代码没分离,当多个线程执行一样的任务时候需要多份认任务代码 </div> <div class="ql-code-block"> * 任务方法没有返回值 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> public static class MyThread extends Thread{ </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> System.out.println("I am a Thread"); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 第二种创建线程的方法 </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * 优点: </div> <div class="ql-code-block"> * 任务跟代码分离开 </div> <div class="ql-code-block"> * 缺点: </div> <div class="ql-code-block"> * 任务方法没有返回值 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> public static class RunableTask implements Runnable{ </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> System.out.println("I am a Thread2"); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public static class CallTask implements Callable&lt;String&gt;{ </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public String call() throws Exception { </div> <div class="ql-code-block"> return "hello Thread"; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public static void main(String[] args) throws InterruptedException{ </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> *创建线程的时候其实还没有成功启动,只有执行了线程的start方法的时候才算真正启动成功 </div> <div class="ql-code-block"> * 用操作系统的角度来看创建线程的时候其实处在了就绪态(已经获取了除了CPU之外的其他资源) </div> <div class="ql-code-block"> * 执行start会执行线程的run方法,就获得了cpu从就绪态转到了运行态 </div> <div class="ql-code-block"> * run方法结束标志该线程运行完成处于终止状态 </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> MyThread myThread = new MyThread(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> //线程启动 </div> <div class="ql-code-block"> myThread.start();; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> RunableTask runableTask = new RunableTask(); </div> <div class="ql-code-block"> new Thread((runableTask)).start();; </div> <div class="ql-code-block"> new Thread((runableTask)).start(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> FutureTask&lt;String&gt; futureTask = new FutureTask&lt;&gt;(new CallTask()); </div> <div class="ql-code-block"> new Thread(futureTask).start(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> String string = futureTask.get(); </div> <div class="ql-code-block"> System.out.println(string); </div> <div class="ql-code-block"> } catch (ExecutionException e) { </div> <div class="ql-code-block"> e.printStackTrace(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h2>线程的通知和等待</h2> <h3><strong>wait()函数</strong></h3> <p><br></p> <p>当一个线程调用共享线程的wait()方法的时候,该线程就会被组赛挂起,知道发生以下几种才会返回</p> <ol> <li data-list="ordered" class="ql-indent-2"><span class="ql-ui"></span>其他线程调用了该共享线程的notify()或者notifyAll()</li> <li data-list="ordered" class="ql-indent-2"><span class="ql-ui"></span>其他线程调用了该线程的interrupt(),该线程抛出了中断异常返回</li> </ol> <p><br></p> <p><strong style="color: rgb(223, 42, 63);">注意:调用wait()方法的时候如果没有提前获取该对象的监视器锁,则调用该方法的时候会抛出IllegalMonitorException异常</strong></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <h4>获取共享变量的监视器锁的方法</h4> <h5><span style="font-size: 15px;">执行synchronized同步代码块时候,使用该共享变量作为参数</span></h5> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> synchronized (共享变量){ </div> <div class="ql-code-block"> //代码逻辑 </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h5><span style="font-size: 15px;">调用该共享变量方法的时候,并且该方法使用了synchronized来进行修饰</span></h5> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> synchronized void add(int a,int b){ </div> <div class="ql-code-block"> //代码逻辑 </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <h4>虚假唤醒</h4> <p><br></p> <p>某个线程没有被其他线程调用notify(),notifyAll()方法进行通知,并且没有被中断,等待超时,就从挂起态变成了运行状态</p> <p>要避免出现这种虚假唤醒</p> <p>方法: 不断的测试该线程被唤醒的条件是否满足,不满足则继续等待,满足唤醒条件了就退出循环</p> <div class="ql-code-block-container"> <div class="ql-code-block"> synchronized (obj){ </div> <div class="ql-code-block"> while (条件不满足){ </div> <div class="ql-code-block"> obj.wait(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <p><strong>注意:当前线程调用共享变量的wait()方法后只会释放当前共享线程变量上的锁,如果当前线程还持有其他线程的锁是不会被释放的.</strong></p> <p><strong>当一个线程调用共享对象的wait()方法被阻塞挂起的时候,如果其他线程中断了该线程,则该线程会抛出InterruptedException异常并返回</strong></p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * @author xingchen </div> <div class="ql-code-block"> * @version V1.0 </div> <div class="ql-code-block"> * @Package com.并发 </div> <div class="ql-code-block"> * @date 2023/4/2 20:16 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> public class WaitNotifyInterupt { </div> <div class="ql-code-block"> static Object object=new Object(); </div> <div class="ql-code-block"> public static void main(String[] args) throws InterruptedException{ </div> <div class="ql-code-block"> Thread threadA=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> System.out.println("--------begin--------"); </div> <div class="ql-code-block"> synchronized (object){ </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> object.wait(); </div> <div class="ql-code-block"> } catch (InterruptedException e) { </div> <div class="ql-code-block"> e.printStackTrace(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> threadA.start(); </div> <div class="ql-code-block"> Thread.sleep(1000); </div> <div class="ql-code-block"> System.out.println("begin interrput threadA"); </div> <div class="ql-code-block"> threadA.interrupt(); </div> <div class="ql-code-block"> System.out.println("end interrupt threadA---"); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h3>wait(long timeout)函数</h3> <p><br></p> <p>相比之前的外滩()方法多了一个超时参数</p> <p>不同点:</p> <p>如果一个线程调用共享对象的该方法挂起时,没有在指定的超时时间内被其他线程调用该共享线程的notify()和notifyAll()方法唤醒,就会因为超时而返回</p> <p>如果调用一个负数则会抛出IllegalArgumentException异常</p> <p><br></p> <p><br></p> <h3>wait(long timeout,int nanos)函数</h3> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> public final void wait(long timeout,int nanos) throws InterruptedException{ </div> <div class="ql-code-block"> if(nanos&lt;0){ </div> <div class="ql-code-block"> throw new IllegalAccessException("timeout value is negative"); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> if(nanos&lt;0||nanos&gt;999999){ </div> <div class="ql-code-block"> throw new IllegalAccessException("nanoscond timeout value out of range"); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> if(nanos&gt;0){ </div> <div class="ql-code-block"> timeout++; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> wait(timeout); </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h3>notify()函数</h3> <p><br></p> <p>会唤醒一个在共享变量上调用的wait()系列方法后被挂起的线程.一个共享变量上可能会有很多个被阻塞的线程在等待,具体唤醒那个线程是随机的</p> <p>被唤醒的线程不是立马就可以从wait()方法返回并运行,该线程必须在获取了对共享对象的监视器锁后才可以返回</p> <p>没有获取到监视器锁的话则会抛出IllegalMonitorStateException异常</p> <p><br></p> <h3>notifyAll()函数</h3> <p><br></p> <p>该方法相比于上一个方法来说,该方法会释放该共享变量上面所有的由于调用wait()系列方法而被挂起的线程</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class NotifyAllTest { </div> <div class="ql-code-block"> private static volatile Object resourceA=new Object(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public static void main(String[] args) throws InterruptedException{ </div> <div class="ql-code-block"> Thread threadA=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> synchronized (resourceA){ </div> <div class="ql-code-block"> System.out.println("threadA get resocuseA lock"); </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> resourceA.wait(); </div> <div class="ql-code-block"> System.out.println("threadA end wait"); </div> <div class="ql-code-block"> } catch (InterruptedException e) { </div> <div class="ql-code-block"> e.printStackTrace(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"> Thread threadB=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> synchronized (resourceA){ </div> <div class="ql-code-block"> System.out.println("threadB get resocuseA lock"); </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> resourceA.wait(); </div> <div class="ql-code-block"> System.out.println("threadB end wait"); </div> <div class="ql-code-block"> } catch (InterruptedException e) { </div> <div class="ql-code-block"> e.printStackTrace(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"> Thread threadC=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> synchronized (resourceA){ </div> <div class="ql-code-block"> System.out.println("thread begin notify"); </div> <div class="ql-code-block"> resourceA.notifyAll(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> threadA.start(); </div> <div class="ql-code-block"> threadB.start(); </div> <div class="ql-code-block"> Thread.sleep(1000); </div> <div class="ql-code-block"> threadC.start(); </div> <div class="ql-code-block"> threadA.join(); </div> <div class="ql-code-block"> threadB.join(); </div> <div class="ql-code-block"> threadC.join(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> System.out.println("over"); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p>注意:在共享变量上调用notifyAll()只会唤醒该方法前调用了wait系列函数而被放入共享变量等待集合里面的线程。而不会唤醒在该方法之后调用wait系列函数。</p> <p>等待线程6373等待</p> <p><br></p> <h2>等待线程执行终止的join方法</h2> <p><br></p> <p>在需要等待多个事件完成后才螚继续往下执行的时候就可以使用Thread方法提供的join()方法</p> <p>该方法是无参无返回值的方法。</p> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * @author xingchen </div> <div class="ql-code-block"> * @version V1.0 </div> <div class="ql-code-block"> * @Package com.并发.Test </div> <div class="ql-code-block"> * @date 2023/4/12 23:31 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> public class JoinTest { </div> <div class="ql-code-block"> public static void main(String[] args) throws InterruptedException{ </div> <div class="ql-code-block"> Thread threadOne=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> Thread.sleep(1000); </div> <div class="ql-code-block"> } catch (InterruptedException e) { </div> <div class="ql-code-block"> e.printStackTrace(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> System.out.println("child threadOne over"); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> Thread threadTwo =new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> Thread.sleep(1000); </div> <div class="ql-code-block"> } catch (InterruptedException e) { </div> <div class="ql-code-block"> e.printStackTrace(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> System.out.println("child threanTwo over"); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> threadOne.start(); </div> <div class="ql-code-block"> threadTwo.start(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> System.out.println("wait all child thread over "); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> threadOne.join(); </div> <div class="ql-code-block"> threadTwo.join(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> System.out.println("all child over"); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p>线程A调用线程B的join方法后会被阻塞,当其他线程调用线程A的interrupt()方法中断线程A,则A会抛出一个InterruptedException异常</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class InterruptedExceptionTest { </div> <div class="ql-code-block"> public static void main(String[] args) throws InterruptedException{ </div> <div class="ql-code-block"> Thread threadOne =new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> System.out.println("threadOne begin run!!!"); </div> <div class="ql-code-block"> for(; ; ){ </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> final Thread mainThread=Thread.currentThread(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> Thread threadTwo =new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> Thread.sleep(1000); </div> <div class="ql-code-block"> } catch (InterruptedException e) { </div> <div class="ql-code-block"> e.printStackTrace(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> mainThread.interrupt(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> threadOne.start(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> threadTwo.start(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> threadOne.join(); </div> <div class="ql-code-block"> } catch (InterruptedException e) { </div> <div class="ql-code-block"> System.out.println("main thread "+e); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h2>让线程睡眠都sleep方法</h2> <p><br></p> <p>sleep是Thread的一个静态方法,调用该方法会暂时让出指定时间的执行权,也就是不参与CPU调度,但是该线程所拥有的监视器资源(锁)还是持有不让出的,指定的休眠时间到了就会正常返回,相当于从阻塞态变成了就绪态,参与CPU的调度,如果获取到CPU资源之后就可以进入运行态。但是如果在睡眠期间其他线程调用了该线程的Interrupt()方法中断了该线程,那么该方法调用sleep的时候会抛出InterruptedException异常而返回。</p> <div class="ql-code-block-container"> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * @author xingchen </div> <div class="ql-code-block"> * @version V1.0 </div> <div class="ql-code-block"> * @Package com.并发.Test </div> <div class="ql-code-block"> * @date 2023/4/12 23:38 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> public class InterruptedExceptionTest { </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> //创建一个独占锁 </div> <div class="ql-code-block"> private static final Lock lock=new ReentrantLock(); </div> <div class="ql-code-block"> public static void main(String[] args) throws InterruptedException { </div> <div class="ql-code-block"> Thread threadOne = new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> lock.lock(); </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> System.out.println("child threadA is in sleep"); </div> <div class="ql-code-block"> Thread.sleep(1000); </div> <div class="ql-code-block"> System.out.println("child threadA is in awaked"); </div> <div class="ql-code-block"> } catch (InterruptedException e) { </div> <div class="ql-code-block"> e.printStackTrace(); </div> <div class="ql-code-block"> } finally { </div> <div class="ql-code-block"> lock.unlock(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> Thread threadTwo = new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> lock.lock(); </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> System.out.println("child threadB is in sleep"); </div> <div class="ql-code-block"> Thread.sleep(1000); </div> <div class="ql-code-block"> System.out.println("child threadB is in awake"); </div> <div class="ql-code-block"> } catch (InterruptedException e) { </div> <div class="ql-code-block"> e.printStackTrace(); </div> <div class="ql-code-block"> } finally { </div> <div class="ql-code-block"> lock.unlock(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> threadOne.start(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> threadTwo.start(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <h2>让出cpu执行权的yield方法</h2> <p><br></p> <p>yield是Thread的一个静态方法,当线程调用yield方法时,就是按时线程调度器当前线程请求让出自己的cpu使用 但是线程调度器可以忽略这个暗示。</p> <p>使用的时候意味着该线程自己占用都时间片没有使用完的部分不想使用了,暗示线程调度器现在就可以进行下一轮的线程调度。当前线程交出cpu使用权,然后处于就绪状态,线程调度器会从线程就绪队列里面获取一个线程优先级最高的线程,也有可能会调度到刚刚让出cpu的那个线程来获取cpu执行权。</p> <p><br></p> <p>总结:</p> <p>sleep与yield方法的区别</p> <p>当线程调用sleep方法时会调用线程会被阻塞挂起指定时间,在这期间线程调度器不会去调度该程。</p> <p>当调用yield时候,线程会让出自己剩余的时间片,并没有被阻塞挂起,而是处于就绪状态,线程调度器在下一次调度的时候就有可能调度刀当前线程执行。</p> <p><br></p> <h2>线程中断</h2> <p><br></p> <p>线程中断是一种线程间协作模式,通过设置线程的中断标志并不能直接终止该线程的执行,而是被中断的线程根据中断状态自行处理。</p> <ol> <li data-list="bullet"><span class="ql-ui"></span>void Interrupt() :中断线程 例如当线程a运行时,线程b可以调用线程a的interrupt()方法设置a的中断标志为true并返回。设置标志仅仅是设置标志,线程a并没有被中断,他会继续往下执行,如果线程a调用了wait系列函数,join方法或者sleep等方法被阻塞挂起,这时候若是线程b调用a的interrupt()方法,线程a会在调用这些方法的时候抛出InterruptedException异常</li> <li data-list="bullet"><span class="ql-ui"></span>boolean isInterrupted() :检测当前线程是否被中断。如果是返回true,否则返回false</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"> public boolean isInterrupted(){ </div> <div class="ql-code-block"> //传递false,说明不清除中断标志 </div> <div class="ql-code-block"> return isIntrrrupted( false); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> </div> <ol> <li data-list="bullet"><span class="ql-ui"></span>boolean interrupted() :检测当前线程是否被中断,如果是返回true。不同点该方法发现当前线程被中断,会清楚中断标志,并且该方法是static方法,可以通过Thread直接调用在interrupted()内部是获取当前线程的中断标志而不是调用interrupted()方法的实例对象的中断标志。</li> </ol> <div class="ql-code-block-container"> <div class="ql-code-block"> public static boolean Interrupted{ </div> <div class="ql-code-block"> //清除中断标志 </div> <div class="ql-code-block"> return currentThread.isInterrupted(true); </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h4>一段Interrupt优雅退出的例子</h4> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> while (!Thread.currentThread().isInterrupted()&amp;&amp;more work to do){ </div> <div class="ql-code-block"> //do more work </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }catch (InterruptedException e){ </div> <div class="ql-code-block"> //thread was interrupted during sleep or wait </div> <div class="ql-code-block"> }finally { </div> <div class="ql-code-block"> //cleanup if required </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h3>根据中断标志判断线程是否终止的例子</h3> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> public static void main(String[] args) throws InterruptedException{ </div> <div class="ql-code-block"> Thread thread=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> while (!Thread.currentThread().isInterrupted()){ </div> <div class="ql-code-block"> System.out.println(Thread.currentThread()+"hello"); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> thread.start(); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> Thread.sleep(1000); </div> <div class="ql-code-block"> System.out.println("main thread interrupt thread"); </div> <div class="ql-code-block"> thread.interrupt(); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> thread.join(); </div> <div class="ql-code-block"> System.out.println("main is over"); </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h3>当线程为了等待一些特定条件到来,一般会调用sleep函数,wait系列函数或者join函数来阻塞挂起当前线程,如果提前满足由阻塞到激活态的条件,这时候可以调用该线程的interrupt方法,强制sleep方法抛出InterruptedException异常而返回,线程恢复到激活状态</h3> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class InterruptedOrIsInterrupted { </div> <div class="ql-code-block"> public static void main(String[] args) throws InterruptedException{ </div> <div class="ql-code-block"> Thread threadOne=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> for(; ;){ </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> threadOne.start(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> threadOne.interrupt(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> System.out.println("isInterrupted:"+threadOne.isInterrupted()); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // System.out.println("isInterrupted"+threadOne.interrupted()); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> System.out.println("isInterrupted:"+Thread.interrupted()); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> System.out.println("isInterrupted:"+threadOne.isInterrupted()); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> threadOne.join(); </div> <div class="ql-code-block"> System.out.println("main thread is over"); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * @author xingchen </div> <div class="ql-code-block"> * @version V1.0 </div> <div class="ql-code-block"> * @Package com.并发 </div> <div class="ql-code-block"> * @date 2023/4/13 9:11 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> public class InterruptedOrIsInterruptedTwo { </div> <div class="ql-code-block"> public static void main(String[] args) throws InterruptedException{ </div> <div class="ql-code-block"> Thread threadOne=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> while (!Thread.currentThread().isInterrupted()) { </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> System.out.println("threadTwo isInterrupted:" + Thread.currentThread().isInterrupted()); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> threadOne.start(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> threadOne.interrupt(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> threadOne.join(); </div> <div class="ql-code-block"> System.out.println("main thread is over"); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h2>理解线程上下文切换</h2> <p><br></p> <p>多线程里面,线程数一般是大于CPU个数的,而每个CPU同一时刻只能被一个线程所使用,为了让用户感觉到多个线程在同时使用的,CPU的分配采用了时间片轮转的策略.每一个线程分配一个时间片,线程在该分配的时间片内占用CPU执行任务,当线程片用完之后,就会处于就绪状态并让出CPU让其他线程占用使用.</p> <p>上下文切换的时候需要保存当前线程的执行现场,当再次执行时根据保存的执行现场信息恢复执行现场</p> <p><br></p> <p>切换时机:</p> <ol> <li data-list="bullet" class="ql-indent-1"><span class="ql-ui"></span>当前线程的CPU时间片使用完处于就绪状态</li> <li data-list="bullet" class="ql-indent-1"><span class="ql-ui"></span>当前线程被其他线程中断的时候</li> </ol> <p><br></p> <h2>线程死锁</h2> <h3>什么是线程死锁</h3> <p><br></p> <p>死锁是指两个或者两个以上的线程在执行过程中,因抢夺资源而造成的互相等待的现象,在无外力的作用下,这些线程会一直等待下去而无法运行</p> <p><br></p> <h4>产生死锁的四个必要条件</h4> <p><br></p> <ol> <li data-list="ordered"><span class="ql-ui"></span>互斥条件</li> </ol> <p>线程对已经获取到的资源进行排他性使用,资源同时只能由一个线程占用,如果此时还有其他线程请求获取该资源,则请求者只能等待,直至占用的资源被释放</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>请求持有条件</li> </ol> <p>指一个线程已经占有至少一个资源,但又提出新的资源请求,而新的资源已经被其他线程占用,所以当先线程被阻塞,但阻塞的同时并并不释放自己已经获得的资源</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>不可剥夺条件</li> </ol> <p>指线程获取的资源在自己使用完之前别的线程不能抢占,只有在自己使用完之后释放了才能被其他线程使用</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>环路等待条件</li> </ol> <p>指发生死锁的时候必然存在一个线程一个资源的环形链</p> <div class="ql-code-block-container"> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * @author xingchen </div> <div class="ql-code-block"> * @version V1.0 </div> <div class="ql-code-block"> * @Package com.并发.Test </div> <div class="ql-code-block"> * @date 2023/4/13 9:35 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> public class DeadLockTest2 { </div> <div class="ql-code-block"> private static Object resourceA=new Object(); </div> <div class="ql-code-block"> private static Object resourceB=new Object(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public static void main(String[] args) throws InterruptedException{ </div> <div class="ql-code-block"> Thread threadA=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> synchronized (resourceA){ </div> <div class="ql-code-block"> System.out.println((Thread.currentThread() + "get ResourceA")); </div> <div class="ql-code-block"> try{ </div> <div class="ql-code-block"> Thread.sleep(1000); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> }catch (InterruptedException e){ </div> <div class="ql-code-block"> e.printStackTrace(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> System.out.println(Thread.currentThread()+"wait get sourceB"); </div> <div class="ql-code-block"> synchronized (resourceB){ </div> <div class="ql-code-block"> System.out.println(Thread.currentThread()+"get ResourceB"); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> Thread threadB=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> synchronized (resourceB){ </div> <div class="ql-code-block"> System.out.println((Thread.currentThread() + "get ResourceB")); </div> <div class="ql-code-block"> try{ </div> <div class="ql-code-block"> Thread.sleep(1000); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> }catch (InterruptedException e){ </div> <div class="ql-code-block"> e.printStackTrace(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> System.out.println(Thread.currentThread()+"wait get sourceA"); </div> <div class="ql-code-block"> synchronized (resourceB){ </div> <div class="ql-code-block"> System.out.println(Thread.currentThread()+"get ResourceA"); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h3><span style="color: rgb(0, 0, 0);">避免线程死锁</span></h3> <p><br></p> <p>只需要破坏至少一个死锁的必要条件即可,但是目前其实能被破坏的只有<strong>请求并持有</strong>和<strong>环路等待条件</strong>是可以被破坏的</p> <p>造成死锁其实也和申请资源的顺序有很大的关系,使用资源的有序性原则就可以破坏避免死锁</p> <p>举例:对上述代码的线程B进行修改</p> <div class="ql-code-block-container"> <div class="ql-code-block"> Thread threadB=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> synchronized (resourceA){ </div> <div class="ql-code-block"> System.out.println((Thread.currentThread() + "get ResourceB")); </div> <div class="ql-code-block"> try{ </div> <div class="ql-code-block"> Thread.sleep(1000); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> }catch (InterruptedException e){ </div> <div class="ql-code-block"> e.printStackTrace(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> System.out.println(Thread.currentThread()+"wait get sourceA"); </div> <div class="ql-code-block"> synchronized (resourceB){ </div> <div class="ql-code-block"> System.out.println(Thread.currentThread()+"get ResourceA"); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> </div> <p><br></p> <h2>守护线程与用户线程</h2> <p><br></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>daemon线程(守护线程)</li> <li data-list="bullet"><span class="ql-ui"></span>user线程(用户线程)</li> </ol> <p>main函数就是属于用户线程JVM启动的时候会调用main线程,但其实还调用了许多其他的守护线程</p> <p><br></p> <h3>两者的区别</h3> <p><br></p> <p>当最后一个非守护线程结束时,JVM会正常退出,而不管当前是否由守护线程,也就是说守护线程不影响JVM的退出.也就是说只要有一个用户线程没有结束,正常情况下JVM就不会退出</p> <div class="ql-code-block-container"> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * @author xingchen </div> <div class="ql-code-block"> * @version V1.0 </div> <div class="ql-code-block"> * @Package com.并发.Test </div> <div class="ql-code-block"> * @date 2023/4/13 16:44 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> public class DaemonTest { </div> <div class="ql-code-block"> public static void main(String[] args) { </div> <div class="ql-code-block"> Thread thread=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 只需要设置参数为true就开启了守护线程 </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> thread.setDaemon(true); </div> <div class="ql-code-block"> thread.start(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h3>举例理解守护线程和用户线程的区别</h3> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class DaemonOrUserTest { </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> *thread线程里面是一个无限循环,运行之后主线程已经结束,但是jvm还没有退出 </div> <div class="ql-code-block"> * 说明父线程结束后,子线程还可以继续存在,也就是子线程的生命周期并不受父线程的影响 </div> <div class="ql-code-block"> * 也说明了在用户线程还存在的情况下JVM进程并不会终止 </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * 设置成用户线程之后一旦主线程停止之后,jvm发现已经不存在用户线程了,就会终止JVN进程 </div> <div class="ql-code-block"> * 如果用户进程已经结束,但是守护线程还说运行,这个时候JVM不需要等待守护线程停止就直接结束JVM进程 </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * main线程运行结束之后,JVM会自动启动一个DestroyJavaVm 的线程,该线程等待所有的用户线程结束后 </div> <div class="ql-code-block"> * 终止JVM进程 </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> public static void main(String[] args) { </div> <div class="ql-code-block"> Thread thread=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> for (; ;){ </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> thread.setDaemon(true); </div> <div class="ql-code-block"> thread.start(); </div> <div class="ql-code-block"> System.out.println("main thread is over"); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h3>总结</h3> <p><br></p> <p>如果希望在主线程结束后JVM立马结束,那么在创建进程的时候可以将其设置为守护线程,否则的话就设置为用户线程</p> <p><br></p> <h2>ThreadLocal</h2> <p><br></p> <p>多线程访问退役个共享变量的时候容易出现并发问题,特别是在多个线程需要对一个共享变量进行写入时,为了保证线程安全,一般使用访问者在访问共享变量时候需要进行适当的同步</p> <p><br></p> <p>同步一般情况下使用<strong>加锁,</strong>但是这种方式加重了使用者的负担.</p> <p><strong>那么可以使用创建一个变量后,每一个线程对其访问的时候访问的是自己线程的变量</strong>,本节的ThreadLocal就是这个作用.</p> <p><strong>ThreadLocal</strong></p> <p>是由JDK提供的,提供了线程本地变量,如果创建了一个ThreadLocal变量,那么访问这个变量的每一个线程都会有这个变量的本地副本</p> <p>当多个线程操作这个变量的时候,实际操作的是自己本地内存的变量,从而避免了线程安全问题</p> <p><br></p> <h3>使用实例</h3> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class ThreadLocalTest { </div> <div class="ql-code-block"> static void print(String str){ </div> <div class="ql-code-block"> System.out.println(str+":"+localVariable.get()); </div> <div class="ql-code-block"> //localVariable.remove(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> static ThreadLocal&lt;String&gt; localVariable=new ThreadLocal&lt;&gt;(); </div> <div class="ql-code-block"> public static void main(String[] agrs){ </div> <div class="ql-code-block"> Thread threadOne=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> localVariable.set("threadOne local variable"); </div> <div class="ql-code-block"> print("threadOne"); </div> <div class="ql-code-block"> System.out.println("threadOne remove after"+":"+localVariable.get()); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"> Thread threadTwo=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> localVariable.set("threadTwo local variable"); </div> <div class="ql-code-block"> print("threadOne"); </div> <div class="ql-code-block"> System.out.println("threadTwo remove after"+":"+localVariable.get()); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> threadOne.start(); </div> <div class="ql-code-block"> threadTwo.start(); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h3>实现原理</h3> <p><br></p> <p>set方法</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public void set(T value){ </div> <div class="ql-code-block"> Thread t=Thread.currentThread(); </div> <div class="ql-code-block"> ThreadLoalMap map=getMap(t); </div> <div class="ql-code-block"> if(map!=null){ </div> <div class="ql-code-block"> map.set(this,value); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> }else { </div> <div class="ql-code-block"> createMap(t,value); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> ThreadLocalMap getMap(Thread t){ </div> <div class="ql-code-block"> return t.threadLocals; </div> <div class="ql-code-block"> } </div> </div> <p><strong> getMap(t)</strong></p> <ol> <li data-list="bullet"><span class="ql-ui"></span>方法的作用是获取线程自己的变量threadLocals,threadlocal变量被绑定到线程的成员变量上面</li> <li data-list="bullet"><span class="ql-ui"></span>如果个体Map(T)返回值不为空,则把value设置到threadLocals</li> <li data-list="bullet"><span class="ql-ui"></span>如果返回值为空则说明第一次调用的set方法,这时候创建当前线程的threadLocals变量</li> </ol> <p><strong>createMap(t,value) </strong></p> <p>创建当前线程的threadLocals变量</p> <div class="ql-code-block-container"> <div class="ql-code-block"> void createMap(Thread t,T firstValue){ </div> <div class="ql-code-block"> t.threadLocals=new ThreadLocalMap(this,firstValue); </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <p><br></p> <h3>T get()方法</h3> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> public void get(){ </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> //获取当前线程 </div> <div class="ql-code-block"> Thread t=Thread.currentThread(); </div> <div class="ql-code-block"> //获取当前线程的threadLocxals变量 </div> <div class="ql-code-block"> ThreadLocalMap map=getMap(t); </div> <div class="ql-code-block"> //如果ThreadLocals不为空,则返回对应的本地变量 </div> <div class="ql-code-block"> if(map!=null){ </div> <div class="ql-code-block"> ThreadLocalMap.Entry e=map.getEntry(this); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> if(e!=null){ </div> <div class="ql-code-block"> @SuppressWarnings("unchecked") </div> <div class="ql-code-block"> T result=(T) e.value; </div> <div class="ql-code-block"> return result; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> //为空初始化当前线程的ThreadLocals成员变量 </div> <div class="ql-code-block"> return setInitialValue; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private T setInitialValue(){ </div> <div class="ql-code-block"> //初始化为null </div> <div class="ql-code-block"> T value =initialValue; </div> <div class="ql-code-block"> Thread t=Thread.currentThread(); </div> <div class="ql-code-block"> ThreadLocalMap map=getMap(t); </div> <div class="ql-code-block"> //如果当前线程的本地变量不为空 </div> <div class="ql-code-block"> if(map!=null){ </div> <div class="ql-code-block"> map.set(this,value); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> }else { </div> <div class="ql-code-block"> //如果当前线程的本地变量为空 则创建 </div> <div class="ql-code-block"> createMap(t,value); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> return value; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> private T initialValue(){ </div> <div class="ql-code-block"> return null; </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h3>void remove()</h3> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 如果当前线程的threadLocals变量不为空,则删除当前线程中指定ThreadLocal实例的本地变量 </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> public void remove(){ </div> <div class="ql-code-block"> ThreadLocalMap map=getMap(Thread.currentThread()); </div> <div class="ql-code-block"> if(m!=null){ </div> <div class="ql-code-block"> m.remove(this); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> } </div> </div> <p></p> <p><br></p> <h3>不支持继承性</h3> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class TestThreadLocal1 { </div> <div class="ql-code-block"> public static ThreadLocal&lt;String&gt; threadLocal=new ThreadLocal&lt;&gt;(); </div> <div class="ql-code-block"> public static void main(String[] args){ </div> <div class="ql-code-block"> threadLocal.set("hello world"); </div> <div class="ql-code-block"> Thread thread=new Thread(new Runnable() { </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run() { </div> <div class="ql-code-block"> System.out.println("thread:"+threadLocal.get()); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"> thread.start(); </div> <div class="ql-code-block"> System.out.println("main:"+threadLocal.get()); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p>同一个ThreadLocal变量在父线程中被设置后,在子线程是获取不到的</p> <p>在子线程里面调用的get方法是当前线程,而调用的set方法是设置的main线程</p> <p>两者是不同的线程</p> <p><br></p> <h3>InheritableThreadLocal类</h3> <p><br></p> <p>继承自ThreadLocal,提供了一个特性,让子线程可以访问在父线程中设置的本地变量</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class InheritableThreadLocal&lt;T&gt; extends ThreadLocal{ </div> <div class="ql-code-block"> protected T childValue(T parentValue){ </div> <div class="ql-code-block"> return parentValue; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> ThreadLocalMap.getMap(Thread t){ </div> <div class="ql-code-block"> return t.inheritableThreadLocals; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> void create(Thread t,T firstValue){ </div> <div class="ql-code-block"> t.inheritableLocals=new TestThreadLocalMap(this,firstValue); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p></p> <p class="image-uploading"><span><span><img src="https://pic.code-nav.cn/planet_post_image/1609216259264610306/d83vfyx3.jpeg"></span></span></p> <p><br></p> <p><br></p> <p></p> <p class="image-uploading"><span><span><img src="https://pic.code-nav.cn/planet_post_image/1609216259264610306/eitdkd8o.jpeg"></span></span></p> <p><br></p> <p>总结:</p> <p>InheritableThreadLocal类重写代码让本地变量保存到了具体线程的inheritableThreadLocals变量里面,那么线程在通过InheritableThreadLocal类实例的set或者get()方法设置变量时,就会创建当前线程的InheritableThreadLocals变量,当父线程创建子线程时,构造函数就会把父线程的InheritableThreadLocals变量里面的本地变量复制一份保存到子线程的InheritableThreadLocals变量里面.</p> <p><br></p> <p><br></p> <h1>并发编程的其他基础知识</h1> <h2>什么是多线程的并发编程</h2> <p><br></p> <p>并发:</p> <p>同一时间段内多个任务同时都在执行,且执行都没有执行结束,强调的是在一个时间段内同时执行,而一个时间段由多个时间积累而成的,所以并发的多个任务在单位时间内并不一定同时执行</p> <p>并行:</p> <p>单位时间内多个任务同时在执行</p> <p><br></p> <h2>为什么要进行多线程并发编程</h2> <p><br></p> <p>多核CPU时代打破了单核CPU对多线程的性能限制,多个CPU"意味着每个线程可以使用自己的CPU运行,这减少了线程上下文切换的开销,但是随着对应用系统性能和吞吐量要求的提高,出现了海量数据和请求的要求,迫切需要高并发编程</p> <p><br></p> <h2>java的线程安全问题</h2> <p><br></p> <p>共享资源:</p> <p>该资源被多个线程所持有或者说多个线程都可以访问该资源</p> <p><br></p> <p>线程安全问题:</p> <p>当多个线程同时读写一个共享资源并且没有任何同步措施的时候,导致出现脏数据或者不可预见结果的其他问题</p> <p><br></p> <h2>java中共享变量的内存可见性问题</h2> <p><br></p> <p>将所有的变量都存放在主内存,当线程使用变量的时候,会把主内存的变量复制到自己的工作空间或者工作内存,线程读写变量操作的是自己工作内存中的变量</p> <p><br></p> <h2>java中的synchronized关键字</h2> <h3>关键字介绍</h3> <p><br></p> <p>java提供的一个原子性内置锁,java的每一个对象都可以把他当作同步锁来使用,这些java内置的使用者看不到的锁被称为内部锁,与叫做监视器锁.</p> <p>也是一种排他锁,也就是一个线程获取了这个锁后,其他线程必须等待该线程释放锁后才能获取该锁</p> <p>java中的线程和操作系统的原生线程一一对应,当阻塞一个线程时,需要从用户态切换到内核态执行阻塞操作,这是很耗时的,<strong>而synchronized的使用就会导致上下文切换</strong></p> <p><br></p> <h3>内存语义</h3> <p><br></p> <p>把在synchronized块内使用到的变量从线程的工作内存中清楚,这样子在synchronized块内使用的变量就不会从线程的工作内存中获取,而是从主内存中获取,退出synchronized块的内存语义是把在synchronized块内对共享变量的修改刷新到主内存</p> <p><strong>而synchronized的使用就会导致上下文切换带来线程调度开销</strong></p> <p><br></p> <h2>java中的volatile关键字</h2> <p><br></p> <p>确保对一个变量的更新对其他线程马上可见,当一个变量使用volatile关键字时,线程在写入变量的时候就不会把值缓存在寄存器或者其他地方,而是会把值刷新到主内存,当其他线程读取该共享变量的时候,会从主内存重新获取最新值,而不是使用当前线程的工作内存中的值</p> <p><br></p> <h3>内存语义</h3> <p><br></p> <p>和synchronized相似,当线程写入了volatile变量值时就等价于线程退出synchronized同步块(把写入工作内存的变量值同步到主内存)读取volatile变量值时就相当于进入了同步块(先清空本地内存变量值,再从主内存获取最新值).</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class ThreadNotSafeIntegerTest { </div> <div class="ql-code-block"> private int value; </div> <div class="ql-code-block"> public int get(){ </div> <div class="ql-code-block"> return value; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> public void set(int value){ </div> <div class="ql-code-block"> this.value=value; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p>synchronized 方法</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class ThreadNotSafeIntegerTest { </div> <div class="ql-code-block"> private int value; </div> <div class="ql-code-block"> public synchronized int get(){ </div> <div class="ql-code-block"> return value; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public synchronized void set(int value){ </div> <div class="ql-code-block"> this.value=value; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p>volatile方法</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class ThreadNotSafeIntegerTest { </div> <div class="ql-code-block"> private volatile int value; </div> <div class="ql-code-block"> public int get(){ </div> <div class="ql-code-block"> return value; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public void set(int value){ </div> <div class="ql-code-block"> this.value=value; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p>volatile并不能保证操作的原子性</p> <p><br></p> <h4>什么时候使用volatile关键字</h4> <p><br></p> <p>写入变量值不依赖变量的当前值</p> <p>因为如果依赖当前值,将获取-计算-写入三步操作,这三步不是原子性操作,而volatile不能保证原子性</p> <p>读写变量值没有加锁</p> <p>因为加锁本身已经保证了内存可见性,这时候不需要啊变量声明为volatile</p> <p><br></p> <h2>java中的原子操作</h2> <p><br></p> <p>一系列操作时,这些操作要么全部执行,要么全部不执行,不存在执行性其中一部分的情况.</p> <p>如下代码就是线程不安全的问题</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class ThreadNotSafeCount { </div> <div class="ql-code-block"> private Long value; </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> public Long get(){ </div> <div class="ql-code-block"> return value; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> public void inc(){ </div> <div class="ql-code-block"> ++value; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p>使用synchronized修饰就可以保证原子性</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class ThreadNotSafeCount { </div> <div class="ql-code-block"> private Long value; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public synchronized Long get(){ </div> <div class="ql-code-block"> return value; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> public synchronized void inc(){ </div> <div class="ql-code-block"> ++value; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h2>java中的CAS操作</h2> <p><br></p> <p>锁在并发处理中占据一席之地,但是当一个线程没有获取到锁时就会被阻塞挂起,导致线程上下文的切换喝调度开销,java提供了非阻塞的volatile关键字来解决共享变量的可见性,一定程度上解决了锁带来的开销问题,但是只能保证共享变量的可见性,不能解决读-改-写等原子性操作</p> <p>CAS即 Compare and Swap 是JDK提供的非阻塞原子性操作,通过硬件保证了比较--更新操作的原子性</p> <p>JDK里面Unsafe提供了一系列的compareAndSwap方法</p> <p><strong>boolean compareAndSwapLong(Object obj,long valueOffset,long expect,long update)方法</strong></p> <p>比较并交换</p> <p>四个参数分别是</p> <ol> <li data-list="bullet" class="ql-indent-2"><span class="ql-ui"></span>对象内存位置</li> <li data-list="bullet" class="ql-indent-2"><span class="ql-ui"></span>对象中变量的偏移量</li> <li data-list="bullet" class="ql-indent-2"><span class="ql-ui"></span>变量预期值</li> <li data-list="bullet" class="ql-indent-2"><span class="ql-ui"></span>新的值</li> <li data-list="bullet" class="ql-indent-1"><span class="ql-ui"></span>如果对熊obj中内存偏移量为valueOffset的变量的值为expect,则使用新的值update置换旧的值expect</li> </ol> <p><br></p> <h3>ABA问题</h3> <p><br></p> <p>关于CAS 操作有个经典的ABA 问题,具体如下:假如线程I使用 CAS 修改初始值为A的变量X,那么线程I会首先去获取当前变量X 的值(为A),然后使用 CAS 操作尝试修改X的值为 B,如果使用CAS 操作成功了,那么程序运行一定是正确的吗?其实未必,这是因为有可能在线程I获取变量X的值A后,在执行 CAS 前,线程I使用CAS修改了变量X的值为 B,然后又使用 CAS 修改了变量X 的值为 A。所以虽然线程I执行 CAS时X的值是A,但是这个A已经不是线程I获取时的A了。这就是 ABA 问题。</p> <p>ABA 问题的产生是因为变量的状态值产生了环形转换,就是变量的值可以从A到B,然后再从B到A。如果变量的值只能朝着一个方向转换,比如A到B,B到C,不构成环形,就不会存在问题。JDK 中的<strong>AtomicStampedReference 类给每个变量的状态值都配备了一个时间戳,从而避免了 ABA 问题的产生。</strong></p> <p><br></p> <h2>Unsafe</h2> <h2>Unsafe类的重要方法</h2> <p><br></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1609216259264610306/hnzptk0p.jpeg"></p> <p><img src="https://pic.code-nav.cn/planet_post_image/1609216259264610306/6wq41gle.jpeg"></p> <p><br></p> <h2>如何使用Unsafe类</h2> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class UnsafeTest { </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> //获取实例 </div> <div class="ql-code-block"> static final Unsafe unsafe=Unsafe.getUnsafe(); </div> <div class="ql-code-block"> //记录state变量在类中的偏移值 </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> static final long stateOffset; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private volatile long state=0; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> static { </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> stateOffset=unsafe.objectFieldOffset(UnsafeTest.class.getDeclaredField("statee")); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> }catch (Exception e){ </div> <div class="ql-code-block"> System.out.println(e.getLocalizedMessage()); </div> <div class="ql-code-block"> throw new Error(e); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public static void main(String[] args) { </div> <div class="ql-code-block"> UnsafeTest unsafeTest=new UnsafeTest(); </div> <div class="ql-code-block"> Boolean success=unsafe.compareAndSwapInt(unsafeTest,stateOffset,0,1); </div> <div class="ql-code-block"> System.out.println(success); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h4>运行结果</h4> <p><br></p> <div class="ql-code-block-container"> <div class="ql-code-block"> @CallerSensitive </div> <div class="ql-code-block"> public static Unsafe getUnsafe() { </div> <div class="ql-code-block"> Class var0 = Reflection.getCallerClass(); </div> <div class="ql-code-block"> if (!VM.isSystemDomainLoader(var0.getClassLoader())) { </div> <div class="ql-code-block"> throw new SecurityException("Unsafe"); </div> <div class="ql-code-block"> } else { </div> <div class="ql-code-block"> return theUnsafe; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public static boolean isSystemDomainLoader(ClassLoader var0) { </div> <div class="ql-code-block"> return var0 == null; </div> <div class="ql-code-block"> } </div> </div> <p>正规渠道不能使用Unsafe方法,要想使用可以用<strong>反射方法</strong>来获取Unsafe的实例方法</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class TestUnsafe { </div> <div class="ql-code-block"> static final Unsafe unsafe; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> static final long stateOffset; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private volatile long state=0; </div> <div class="ql-code-block"> static { </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> Field file=Unsafe.class.getDeclaredField("theUnsafe"); </div> <div class="ql-code-block"> file.setAccessible(true); </div> <div class="ql-code-block"> unsafe=(Unsafe) file.get(null); </div> <div class="ql-code-block"> stateOffset=unsafe.objectFieldOffset(TestUnsafe.class.getDeclaredField("state")); </div> <div class="ql-code-block"> }catch (Exception e){ </div> <div class="ql-code-block"> System.out.println(e.getLocalizedMessage()); </div> <div class="ql-code-block"> throw new Error(e); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public static void main(String[] args) { </div> <div class="ql-code-block"> TestUnsafe test=new TestUnsafe(); </div> <div class="ql-code-block"> Boolean success=unsafe.compareAndSwapInt(test,stateOffset,0,1); </div> <div class="ql-code-block"> System.out.println(success); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <h2>java指定重排序</h2> <p><br></p> <p>java内存模型允许编译器喝处理器对指令重排序以提高运行性能,并且只会对不存在数据依赖性的指令重排序,在单线程下重排序可以保证最终的执行结果与程序顺序执行结果一致,但是在多线程下存在安全问题</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class MoreThreadTest { </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 这段代码没有声明volatile变量,也没有使用任何同步措施 </div> <div class="ql-code-block"> * 多线程下存在共享内存可见性问题 </div> <div class="ql-code-block"> * 可以通过对共享变量声明成volatile就可以避免指令重排序问题 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> public static class ReadThread extends Thread{ </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run(){ </div> <div class="ql-code-block"> while (!Thread.currentThread().isInterrupted()){ </div> <div class="ql-code-block"> if(ready){ </div> <div class="ql-code-block"> System.out.println(num+num); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> System.out.println("read thread"); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> public static class WriteThread extends Thread{ </div> <div class="ql-code-block"> @Override </div> <div class="ql-code-block"> public void run(){ </div> <div class="ql-code-block"> num=2; </div> <div class="ql-code-block"> ready=true; </div> <div class="ql-code-block"> System.out.println("writeThread set over"); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> private static int num =0; </div> <div class="ql-code-block"> private static boolean ready=false; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public static void main(String[] args) throws InterruptedException{ </div> <div class="ql-code-block"> ReadThread readThread=new ReadThread(); </div> <div class="ql-code-block"> readThread.start(); </div> <div class="ql-code-block"> WriteThread writeThread=new WriteThread(); </div> <div class="ql-code-block"> writeThread.start(); </div> <div class="ql-code-block"> Thread.sleep(1000); </div> <div class="ql-code-block"> readThread.interrupt(); </div> <div class="ql-code-block"> System.out.println("main exit"); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> </div> <p><strong>当volatile变量时,可以确保volatile写之前的操作不会被编译器重排序到volatile写之后,读volatile变量时,可以确保volatile读之后的操作不会被编译器重排序到volatile读之前</strong></p> <p><br></p> <h2>伪共享</h2> <h3>伪共享是什么</h3> <p><br></p> <p>为了解决计算机系统中内存与CPU之间运行速度差问题,会在CPU与主内存之间添加一级或者多级高速缓存存储器(Cache)</p> <p>一般被集成在CPU内部(CPU Cache)</p> <p><br></p> <p>在Cache内部是按行存储的,其中一行称为一个Cache行,Cache行是Cache与主内存进行数据交换的单位,每行的大小一般为2的幂次方字节</p> <p><br></p> <p>当CPU访问某个变量的时候,首先回去看CPU Cache内是否存在变量,如果有则直接从其中获取否则就去主内存中获取变量,然后把该变量所在的内存区域的一个Cache行大小的内存复制到Cache,由于存放到Cache行的内存块而不是单个变量,所以可能把多个变量存放到同一个Cache中,当多个线程同时修改一个缓存行的里面的多个变量的时候,由于同时只能有一个线程操作缓存行,所以相比将每个变量放到一个缓存行,性能会有所下降,这就是伪共享</p> <p><br></p> <p>多个线程不能同时去修改自己所使用的CPU中相同缓存行里面的变量,更坏的情况是,如果CPU只有一级缓存,则会导致频繁的访问主内存</p> <p><br></p> <p><br></p> <h3>为什么会出现伪共享</h3> <p><br></p> <p>是因为多个变量被放入了一个缓存行,并且多个线程同时去写入缓存行中不同的变量</p> <p>原因:</p> <p>因为缓存与内存交换的数据的单位就是缓存行,所以多个变量会被放入同一个缓存行</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class ForContentTest { </div> <div class="ql-code-block"> static final int LINE_NUM=1024; </div> <div class="ql-code-block"> static final int COLUM_NUM=1024; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public static void main(String[] args) { </div> <div class="ql-code-block"> long[][] array=new long[LINE_NUM][COLUM_NUM]; </div> <div class="ql-code-block"> long startTime =System.currentTimeMillis(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> for (int i=0;i&lt;LINE_NUM;++i){ </div> <div class="ql-code-block"> for (int j=0;j&lt;COLUM_NUM;++j){ </div> <div class="ql-code-block"> array[i][j]=i*2+j; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> long endTime=System.currentTimeMillis(); </div> <div class="ql-code-block"> long cacheTime=endTime-startTime; </div> <div class="ql-code-block"> System.out.println("cache time :"+cacheTime); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> </div> <p></p> <div class="ql-code-block-container"> <div class="ql-code-block"> public class ForContentTest2 { </div> <div class="ql-code-block"> static final int LINE_NUM=1024; </div> <div class="ql-code-block"> static final int COLUM_NUM=1024; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public static void main(String[] args) { </div> <div class="ql-code-block"> long[][] array=new long[LINE_NUM][COLUM_NUM]; </div> <div class="ql-code-block"> long startTime =System.currentTimeMillis(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> for (int i=0;i&lt;LINE_NUM;++i){ </div> <div class="ql-code-block"> for (int j=0;j&lt;COLUM_NUM;++j){ </div> <div class="ql-code-block"> array[j][i]=i*2+j; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> long endTime=System.currentTimeMillis(); </div> <div class="ql-code-block"> </div> <div class="ql-code-block"> System.out.println("no cache time :"+(endTime-startTime)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p>代码一比代码二快了不少原因</p> <p>数组中数组元素的内存地址是连续的,当访问数组的第一个元素的时,会把第一个元素后的若干个元素一块存入缓存行,这样子顺序访问数组里面的元素时直接命中,就不会去主内存读取了.</p> <p>第二个则是跳跃式的访问数组元素,不是顺序的,这样子破坏了程序访问的局部性原则,并且缓存是容量控制的,当缓存满了会根据一定淘汰算法替换缓存行,这会导致从内存置换过来的缓存行的元素还没等读取到就被置换了</p> <p><br></p> <p>所以在单个线程下顺序修改一个缓存行中的多个变量,会充分利用程序员运行的局部性原则,从而加速程序的运行,而在多线程下修改一个缓存行的多个变量时候就会竞争缓存行,从而降低程序运行性能</p> <p><br></p> <p><br></p> <h3>如何避免伪共享</h3> <p><br></p> <p>通过字节填充的方式来避免该问题,也就是创建一个变量时使用填充字段填充改变量所在的缓存行,这样就避免了将多个变量存放在同一个缓存行中</p> <div class="ql-code-block-container"> <div class="ql-code-block"> public final static class FilledLong{ </div> <div class="ql-code-block"> public volatile long value; </div> <div class="ql-code-block"> public long p1,p2,p3,p4,p5,p6; </div> <div class="ql-code-block"> } </div> </div> <p>假设缓存行为64个字节'</p> <p>这六个变量加上volatile变量以及一个FilledLong类对象一共占用64个字节正好是可以存入同一个缓存行</p> <p><img src="https://pic.code-nav.cn/planet_post_image/1609216259264610306/t64jxgqg.jpeg"></p> <p></p> <p class="image-uploading"><span></span></p> <p><br></p> <h3>小结</h3> <p><br></p> <p>本节讲述了伪共享如何产生,如何避免,并证明在多线程下访问同一个缓存行的多个变量时才会出现伪共享,在单线程下访问一共缓存行里面的多个变量反而会对程序运行起到加速作用</p> <p><br></p> <h2>锁的概述</h2> <h3>乐观锁与悲观锁</h3> <p><br></p> <p>悲观锁:</p> <p>对数据被外界修改保持保守态度,认为数据很容易被其他线程修改,所以在数据被处理之前先对数据进行加锁,并在整个数据处理过程中,使数据处于锁定状态</p> <p>实现:</p> <p>往往依赖数据库提供的锁机制,即数据库中,在对数据记录操作前给记录加排他锁,如果获取锁失败,则说明数据正在被其他线程修改,当前线程则等待或者抛出异常,如果获取锁成功,则对记录进行操作,然后提交事务后释放排他锁</p> <p>乐观锁:</p> <p>数据在一般情况下不会造成冲突,所以在访问记录前不会加排他锁,而是在数据提交更新时候,才会正式对数据冲突与否进行检测.</p> <p><br></p> <h3>公平锁和非公平锁</h3> <p><br></p> <p>根据线程获取锁的抢占机制,锁可以分为公平锁和非公平锁</p> <p>公平锁:</p> <p>表示线程获取锁的顺序是按照线程请求锁的时间早晚来决定的,也就是最早请求锁的线程最早获取到锁</p> <p>非公平锁:</p> <p>不一定先到先得</p> <p><br></p> <p>ReentrantLock提供了公平锁和非公平锁的实现</p> <ol> <li data-list="ordered"><span class="ql-ui"></span>公平锁:ReentrantLock pairLock=new ReentrantLock(true)</li> <li data-list="ordered"><span class="ql-ui"></span>非公平锁:ReentrantLock pairLock=new ReentrantLock(false)</li> </ol> <p>如果不传递参数默认是非公平锁</p> <p>公平锁会带来性能的开销</p> <p><br></p> <h3>独占锁和共享锁</h3> <p><br></p> <p>根据锁只能被单个线程持有还是能被多个线程共同持有,锁可以分为独占锁和共享锁</p> <p><strong>独占锁:</strong></p> <p>能保证任何时候都只能有一个线程得到锁ReentrantLock就是以独占锁方式实现的</p> <p>是一种悲观锁,由于每次访问资源都先加上互斥锁,限制了并发性,因为操作并不会影响数据的一致性,而独占锁只允许同一时间由一个线程读取数据,其他线程必须等待当前线程释放锁才能进行读取</p> <p><strong>共享锁:</strong></p> <p>ReadWriteLock读写锁,允许一个资源被多个线程同时进行读操作是一种乐观锁,放宽了加锁的条件,允许多个线程同时进行读操作</p> <p><br></p> <h3>什么是重入锁</h3> <p><br></p> <p>当一个线程要获取一个被其他线程持有的独占锁时候,该线程会阻塞,</p> <p>那么当一个线程再次获取他自己已经获取的锁时候是否会被阻塞呢?</p> <p>如果不被阻塞那么该锁是可重入的,也就是说只要该线程获取了该锁,就可以无限次地进入该锁锁住的代码</p> <p><br></p> <h3>自旋锁</h3> <p><br></p> <p>当前线程在获取锁的时候,如果发现锁已经被其他线程占用,不会马上阻塞自己,在不放弃CPU的使用权的情况下,多次尝试获取(默认次数10)很有可能后面几次尝试中其他线程已经释放了锁,如果尝试指定次数之后仍然没有获取到锁则当前线程则会被阻塞挂起,自旋锁是适应了CPU时间旱区线程阻塞与调度的开销,但是很有可能这些CPU时间白白浪费啦</p> <p><br></p> <h3>总结</h3> <p><br></p> <p><span style="color: rgb(223, 42, 63);">本章主要讲述并发编程的基础知识,为后面在高级篇讲解并发包源码打下了基础并结合图示形象的讲述了为什么要使用多线程编程,多线程编程存在的线程安全问题,以及什么是内存可见性问题,然后讲述了synchronized和volatile关键字,并且强调前者既保证内存的可见性又保证了原子性,后者则主要保存了内存可见性,但是二者的内存语义非常相似,最后讲解了什么是CAS和线程间同步以及各组锁概念</span></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <p></p> <p><br></p> <p><br></p> </div> </body> </html>

下载 APP