【第二章:Java核心技术解析】第9节:Java进阶 - 高效并发编程(中)
大家好,很高兴我们可以继续学习交流Java高频面试题。在上一小节中,我们介绍了一些多线程并发编程的基础高频考察知识点,本小节,我们继续来交流学习多线程的相关知识点,主要包括原子性,可见性,有序性;常用的同步锁synchronized关键字,轻量级锁volatile关键字以及显式锁ReentrantLock等。
我们知道,多线程并发利用了CPU轮询时间片的特点,在一个线程进入阻塞状态时,可以快速切换到其余线程执行其余操作。CPU轮询时间片有利于提高其资源的利用率,最大限度的利用系统提供的处理能力,有效减少了用户的等待响应时间。但是多线程并发编程也存在着线程活性故障以及如何保证线程安全的问题。
在上一小节中,我们阐述了何为线程活性故障。本小节中,主要对线程安全相关知识点进行阐述。我们先来看一个线程安全的经典问题:多个窗口售票问题。
Demo展示如下:
package niuke.thread; public class Demo { public static void main(String[] args) { TicketSale ticketSale = new TicketSale(); Thread Sale1 = new Thread(ticketSale, "售票口1"); Thread Sale2 = new Thread(ticketSale, "售票口2"); Thread Sale3 = new Thread(ticketSale, "售票口3"); Thread Sale4 = new Thread(ticketSale, "售票口4"); // 启动线程,开始售票 Sale1.start(); Sale2.start(); Sale3.start(); Sale4.start(); } } class TicketSale implements Runnable { int ticketSum = 100; @Override public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // 有余票,就卖 while (ticketSum > 0) { System.out.println(Thread.currentThread().getName() + "售出第" + (100 - ticketSum + 1) + "张票"); ticketSum--; } System.out.println(Thread.currentThread().getName() + "表示没有票了"); } }
程序输出部分截图如下:
由图中可以看出,在多线程并发情况下,出现了同一张票被多个窗口卖出的情况,也就是出现了线程安全的问题。多线程环境下的线程安全主要体现在原子性,可见性与有序性方面。接下来,我们依次介绍三大特性。
(1)原子性,可见性与有序性:
答:多线程环境下的线程安全主要体现在原子性,可见性与有序性方面。
原子性:
定义:对于涉及到共享变量访问的操作,若该操作从执行线程以外的任意线程来看是不可分割的,那么该操作就是原子操作,该操作具有原子性。即,其它线程不会“看到”该操作执行了部分的中间结果。
举例:银行转账流程中,A账户减少了100元,那么B账户就会多100元,这两个动作是一个原子操作。我们不会看到A减少了100元,但是B余额保持不变的中间结果。
原子性的实现方式:
- 利用锁的排他性,保证同一时刻只有一个线程在操作一个共享变量
- 利用CAS(Compare And Swap)保证
- Java语言规范中,保证了除long和double型以外的任何变量的写操作都是原子操作
- Java语言规范中又规定,volatile关键字修饰的变量可以保证其写操作的原子性
关于原子性,你应该注意的地方:
- 原子性针对的是多个线程的共享变量,所以对于局部变量来说不存在共享问题,也就无所谓是否是原子操作
- 单线程环境下讨论是否是原子操作没有意义
- volatile关键字仅仅能保证变量写操作的原子性,不保证复合操作,比如说读写操作的原子性
可见性:
定义:可见性是指一个线程对于共享变量的更新,对于后续访问该变量的线程是否可见的问题。
为了阐述可见性问题,我们先来简单介绍处理器缓存的概念。
现代处理器处理速度远大于主内存的处理速度,所以在主内存和处理器之间加入了寄存器,高速缓存,写缓冲器以及无效化队列等部件来加速内存的读写操作。也就是说,我们的处理器可以和这些部件进行读写操作的交互,这些部件可以称为处理器缓存。
处理器对内存的读写操作,其实仅仅是与处理器缓存进行了交互。一个处理器的缓存上的内容无法被另外一个处理器读取,所以另外一个处理器必须通过缓存一致性协议来读取的其他处理器缓存中的数据,并且同步到自己的处理器缓存中,这样保证了
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
<p> Java开发岗高频面试题全解析,专刊正文共计31节,已经全部更新完毕。专刊分9个模块来对Java岗位面试中的知识点进行解析,包括通用面试技能,Java基础,Java进阶,网络协议,常见框架以及算法,设计模式等。专刊串点成面的解析每个面试题背后的技术原理,由浅入深,循序渐进,力争让大家掌握面试题目的背后的技术原理,摒弃背题模式的陋习。 专刊详细信息,请查阅专刊大纲和开篇词的介绍。 本专刊购买后即可解锁所有章节,故不可以退换哦~ </p> <p> <br /> </p>