* ?线程存在哪些状态??
* 新建 ➩ start() 就绪 ➩︎ 得到处理器资源 ➩ 运行 ➩ 死亡
*
* ?创建线程存在哪几种方式?
* 1.继承Thread类
* 2.实现Runnable接口
* 3.实现Callable接口{
@link com.run.threads.callable.CallableDemo}
* 4.线程池 {
@link com.run.threads.pool.ThreadPoolDemo}
*
* ?实现Runnable接口和Callable接口有什么区别?
* • Runnable 接口不会返回结果;
* • Callable 接口可以返回结果;
* • Callable 需要依赖Future Task实现类的支持,用来接收运算结果;
* • Runnable 无法抛出经过检查的异常;
* • Runnable 对象和 Callable 对象的相互转换。
*
* ? 线程的start和run有和区别??<线程启动-操作>
* ◆ 通过调用Thread类的start()方法来启动一个线程,这时此线程是处于就绪状态,并没有运行;
* ◆ 方法run()称为线程体,它包括了要执行的这个线程的内容,线程就进入运行状态,开始运行run()方法体当中的代码;
* --run方法运行结束此线程终止。然后CPU再调度其他线程。「(直接调用run()方法的话,其实根本就没有启动线程的)」{
@link com.run.threads.thread.StartRunTest}
*
* ? sleep()和wait()方法有何区别??<线程等待-操作>
* ◆ 对于sleep()方法,方法是属于Thread类中的。而wait()方法则是属于Object类中的;
* ◆ sleep()方法导致了程序暂停执行指定的时间,让出CPU给其它线程,但是它的监控状态依然保持着,当指定的时间到期了又会自动恢复运行状态;(监控状态就是指定锁的一个概念)
* ◆ 在调用sleep()方法的过程中,线程不会释放对象锁。而当调用wait()方法的时候,线程会放弃对象锁进入等待状态,只有针对此对象调用notify()方法后本线程才会重新尝试执行;
* ◆ sleep()方法可以在任何地方使用,但wait()方法只能在Synchronized方法或者Synchronized块中使用。
* ◆ {
@link com.run.threads.thread.WaitAndSleepTest}
*
* ? notify()与notifyAll()有什么区别??<线程唤醒-操作>
* ◆ Object类中notify()方法,唤醒在此对象监视器上等待的单个线程,如果所有线程都在此对象上等待,则会随机选择唤醒其中一个线程。
* ◆ notifyAll()会让处于等待池的所有线程进入锁池去竞争得到锁的机会。
*
* ? 说一下 yeiId()-线程让步操作 <线程让步-操作>
* ◆ 当调用Thread.yeiId()方法时,会给线程调度器一个当前线程愿意让出CPU执行时间片的暗示,
* --但是线程调度器可能会忽略这个暗示。但是调用yieId()对锁是没有影响的。
* ◆ Thread.yieId()会使当前线程让出CPU执行时间片,与其它线程一起重新竞争CPU时间片。
* --一般情况下,优先级高的线程有更大的可能性成功竞争得到CPU时间片,但这又不是绝对的,
* --有的操作系统对线程优先级并不敏感。{
@link com.run.threads.thread.YeiIdTest}
*
* ? 生产环境下多线程编程是用stop()中断线程的吗??? <线程中断-操作> stop() 与 Interrupt()
* ◆ 直接调用该线程的stop()方法来结束该线程一该方法通常容易导致死锁,不推荐使用。
* ✯ 一般都是使用 'Interrupt()方法来中断线程的'。
* ▵ 调用 Interrupt(),只是通知了线程应该中断了,这个线程本身并不会因此而改变状态。
* ▵ 如果这个线程调用sleep()而使线程处于TIMED-WATlNG阻塞的状态,那么调用Interrupt()线程将立即退出被TIMED-WATlNG阻塞的状态,并抛出InterruptedException异常。
* ▵ 许多声明抛出InterruptedException 的方法(如 Thread.sleep(long mills 方法)),抛出异常前,都会清除中断标识位,所以抛出异常后,调用isInterrupted()方法将会返回false.
* ▵ 想要使用interrupt()方法终止一个线程需要被调用的线程配合中断。{
@link com.run.threads.thread.InterruptTest}
*
* ? 主线程中,获取子线程的返回值有那些方式??
* ◆ Callable 接口方式
* ◆ 循环等待方式、或 join方式 {
@link com.run.threads.thread.GetSubThreadTest}
*
*
* ?? 多线程需要保证的三大核心??
* ▸ 原子性
* ▸ 顺序性
* ▸ 可见性
*
* ?? 如何保证?
* ▸ Synchronized 关键字
* ▸ CAS (原子性)
* ▸ Volatile 关键字 (可见性、顺序性)
*
* ?? 什么是CAS,CAS会存在哪些问题?(原子性)
* ◆ CAS(Compare-And-Swap) 是一种硬件对并发的支持
* --针对处理器操作而设计的处理器总的一种特殊指令[比如:i++]
* --用于管理对共享数据的并发访问.
*
* ◆ CAS 是一种无锁的非阻塞算法的实现.
* --CAS 包含了3种操作数:需要读写的内存值V、进行比较的值A、拟写入的新值B、
* --当且仅当V的值等于A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作.{
@link com.run.threads.multi.CASCase}
*
* ◆ 当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。
* --失败的线程不会被挂起,仅是被告知失败,并且允许再次尝试,
* --当然也允许失败的线程放弃操作(由开发者自己决定).
* -基于这样的原理,CAS操作即使没有锁,也可以发现其它线程对当前线程的干扰,并进行恰当的处理.
*
* ▸ CAS 会存在 ABA 问题.
*
* ?? 为什么存在多线程的可见行的问题?
* ‣ 1、内存可见行(Memory Visibility)是指当某个线程正在使用对象状态而另一个线程在同时修改该状态,
* --需要确保当一个线程修改了对象状态后,其它线程能够看到发生的状态变化。
* ‣ 2、之所以存在可见行问题是由于Java内存模型(Java Memory Model)JMM决定的。
* ‣ 3、Volatile 关键字可以解决可见行问题。{
@link com.run.threads.multi.VolatileCase}
*
* ?? 为什么存在多线程的顺序性的问题?
* ‣ 原因,指令重排是计算机底层堆执行指令的一种优化策略,
* ‣ Volatile 关键字可以执行指令重排,比synchronized 关键字更轻量级的同步锁,
* --利用了内存屏障(Memory barrier)保证特定操作的执行顺序,
* -通过插入内存屏障指令,禁止在内存屏障前后的指令执行重排序优化。
*
* ✭ Volatile 关键字
* ☛ 可以保证顺序性和可见性,但是保证不了原子性。
* ☛ 可以用 Volatile 实现一个双重检查锁的单例模式
* Singleton singleton= new Singleton();
* 1、分配内存空间.
* 2、初始化对象.
* 3、将singleton对象指向分配的内存地址.{
@link com.singleton.patterns.demo.VolatileSingletonDemo}
*
*
* ??说一下如何在高并发环境下保证线程安全的??
* ▴ 主要以 对线程安全问题的诱因 以及 多线程的锁机制 的理解来说;
* ▵ 线程安全问题的主要 诱因:
* -存在共享数据[临界资源]、
* -存在多个线程共同操作这些共享数据、
* ▵ 解决问题的方法
* -同一时刻仅有一个线程在操作共享数据,即加锁.(Synchronized加锁、及Lock同步锁)
*
* ?Synchronized 锁?
* ◇ synchronized 可以把任意一个非Null的对象当作锁.它属于独占式的悲观锁,同时属于可重入锁。
* ◇ synchronized 根据获取的锁分类,可以分为对象锁和类锁。
*
* ❖ Synchronized ,获取对象锁?
* ➣ 同步代码块:synchronized(this、类实例对象锁住的是括号中的实例对象)
* ➣ 同步非静态(static)方法 synchronized Method,锁是对当前对象的实例对象。
*
* ❖ Synchronized ,获取类锁?
* ➣ 同步代码块 synchronized(类名.class),锁住的是小括号中的的类对象(Class对象).
* ➣ 同步静态(static)方法 synchronized static method,锁的是当前对象的类对象(Class对象).
*
* ?Synchronized 的底层实现原理,描述一下??
* ☞ synchronized 同步语句块的实现使用的是, MONITORENTER(monitor/监控器 enter/进入) 和 MONITOREXIT(monitor/监控器 exit/退出),两个指令;
* ☞ monitor对象存在于,每个Java对象的对象头中;
* ☞ 当执行 MONITORENTER(monitor/监控器 enter/进入) 指令时,线程试图获取锁也就是获取 monitor/监控器 的持有权;
* ☞ 当 monitor/监控器,计数器为 '0' 则可以成功获取,获取后将锁计数器设置 '1',也就是加 1;
* ☞ 相应的在执行 MONITOREXIT(monitor/监控器 exit/退出) 指令后,将锁计数器设为 '0',表明锁被释放.
* ☞ 如果获取对象失败,那就当线程就要阻塞等待,直到锁被另外一个线程释放为止.
*
* {
@link com.run.threads.synch.SynchronizedDemo}
*
* ? Synchronized 修饰方法
* ‣ synchronized 修饰的方法并没有 MONITORENTER(monitor/监控器 enter/进入) 和 MONITOREXIT(monitor/监控器 exit/退出),两个指令;
* --取得代之的是 AccSynchronized 标识.
* ◆ 通过该 AccSynchronized 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。
* ◆ 当方法调用时,调用指令将会检查方法的,ACC_Synchronized 访问标志是否被设置,如果设置了,执行线程将先获取 Monitor, 获取成功之后才能执行方法体
* --方法执行完后再释放 Monitor. 在方法执行期间,其它任何线程都无法再获得同一个 Monitor/监控器 对象.
* ◆ 两种同步方式本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。
* --两个指令的执行是JVM通过调用操作系统的互斥原语 Mutex 来实现。
*
* ?? Synchronized 和 Volatile 关键字的区别??
* ► 在某些特定的应用场景下,Synchronized 和 Volatile 可以完成相同的场景,但是二者之间又有很多区别 主要表现在一下几个方面:
* ▴ Volatile 本质是在告诉 JVM 当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;
* -Synchronized 则是锁定当前变量,只有对当前线程可以访问该变量。
* ▴ Volatile仅适用在变量级别; Synchronized则可以使用在变量、方法和类级别;
* ▴ Volatile标记的变量不会被编辑器优化,Synchronized标记的变量可以被编辑器优化;
* ▴ 多线程访问 Volatile 关键字不会发生阻塞,而Synchronized关键字可能会发生阻塞;
* ▴ Volatile关键字能保证数据的可见性,但不能保证数据的原子性,Synchronized 关键字都能保证。
*
* JUC JUC JUC JUC JUC JUC
* ❖ 除了,Synchronized 可以加锁还了解其它的方式同步吗?
* ◆ JUC工具包了,{
@linkplain java.util.concurrent JUC工具包,这是一个处理线程的工具包。}
* -这个工具包中有一ReentrantLock类实现类 Lock接口,并提供了与Synchronized相同的互斥性和内存可见性。
* -但相较于 Synchronized 提供了更高的处理锁的灵活性.提供了更高的处理锁的灵活性。
*
* ❖ AQS
* ◇ ReentrantLock、ReentrantReadWriteLock底层都是基于AQS来实现的;
* ◇ AQS:(AbstractQueuedSynchronizer,抽象队列同步器),它是一个并发包的基础组件,用来实现各种锁,各种同步组件的。
* --它包含了, state变量、加锁线程、等待队列并发中的核心组件。
*
* ❖ Synchronized 和 ReentrantLock 的比较 ?
* ► 两者的共同点:
* ⁌ 都是用来协调多线程对共享对象、变量的访问;
* ⁌ 都是可重入锁,同一线程可以多次获得同一个锁;
* ⁌ 都是保证可见性和互斥性;
* ► 不同点:
* ⁌ 1、ReentrantLock显示的获得、释放锁、synchronized 隐式获得释放锁;
* ⁌ 2、ReentrantLock 相比 synchronized 的优势是可响应、可轮回、可中断;
* ⁌ 3、ReentrantLock 是API级别的同步非阻塞锁,乐观锁策略;Synchronized 是JVM级别的同步阻塞锁,悲观锁策略。
* ⁌ 4、ReentrantLock 可以实现公平锁 Lock lock = new ReentrantLock(boolean f);
* ⁌ 5、ReentrantLock 通过Condition,可以绑定多个条件;
* ⁌ 6、synchronized 在发生异常时,会自动释放线程占有的锁;而Lock需要手动释放锁。
* ⁌ 7、通过Lock可以知道有没有成功获取锁,而Synchronized却无法办到。
* ⁌ 8、Lock可以提高多个线程进行读写操作的效率,既就是实现读写锁等。
* #使用ReentrantLock类,完成三个线程,每个线程的ID,分别是 A、B、C ,完成线程同步交互的问题。
* {
@link com.run.threads.synch.ReentrantLockSynchDemo}
*
* ??线程池,包含哪些内容?
* ▸ 线程池管理器: 用于创建并管理线程池;
* ▸ 工作线程:线程池中的线程;
* ▸ 任务接口:每个任务必须实现的接口,用于工作线程调度其运行;
* ▸ 任务队列: 用于存放待处理的任务,提供一种缓存机制;
*
*
* ?? 线程池的创建方式,有哪几种?
* ▸ 1、newFixedThreadPool(): 创建固定大小的线程池,可以进行自动线程回收;
* ▸ 2、newCachedThreadPool(): 缓存线程池,线程数量不固定可以根据需求自动的更改数量;
* ▸ 3、newSingleThreadExecutor():创建单个线程池,线程池中只有一个线程;
* ▸ 4、newScheduledThreadPool():创建固定大小的线程,可以延迟或定时的执行任务;
* ▸ 5、WorkStealingPoll(): 内部会创建ForkJoinPool,利用working-stealing算法,并行地处理任务。
*
* ??ForkJoinPool类(线程池)的工作原理是什么?
* ▹ 就是在将一个大任务,进行拆分(fork)成若干个小任务,再将一个个的小任务运算的结果进行 join 汇总。
* ▹ Work-stealing算法:某个线程冲其它队列里窃取任务来执行。
*
* ??ForkJoinPool 和 普通的 ThreadPool 有什么区别?
* ➣ 相对一般的线程池实现,fork/join框架的优势,体现在对其中包含的任务的处理方式上,
* ➣ 一般的线程池,如果一个线程正在执行的任务,由于某些原因无法继续运行,那么该线程会处于等待状态。
* ➣ 在fork/join 框架实现中,如果某个字问题由于等待另外一个字问题的完成,而无法继续运行。
* -那么处理该子问题的线程,会主动寻找其它,尚未运行的字问题来执行。
* ➣ 这种方式减少了线程的等待时间,提高了性能。
*
* ??<线程通信>
* ?在多线程情况下,如何处理线程间协调通信的?
* ◆ 线程的 wait()、notify、join、等API,可以实现线程协调通信的功能。
* ◆ JUC {
@linkplain java.util.concurrent JUC工具包} 中也有很多成熟的线程通信协调工具;
* ◇ Condition {
@linkplain java.util.concurrent.locks.Condition 条件-工具类}
* ◇ CountDownLatch {
@linkplain java.util.concurrent.CountDownLatch 统计向下占有-工具类}
* ◇ CyclicBarrier {
@linkplain java.util.concurrent.CyclicBarrier 环绕屏障-工具类}
* ◇ Semaphore {
@linkplain java.util.concurrent.Semaphore 信号量-工具类}
*
*
* ??描述一下,线程通信中的,生产者和消费者(Producer-Consumer)问题,有哪些解决方案?
* ◆ 生产者-消费者问题,也称有,限缓冲问题(Bounded-buffer).
* ◆ 生产者的主要作用是生成一定量的数据放到缓存区中,然后重复此过程.
* ◆ 与此同时,消费者也在缓存区消耗这些数据。该问题的关键就是保证生产者不会在缓冲区,满时加入数据,消费者也不会在缓冲区中空时消耗数据。
* 实现方式列出两种来实现:
* ▴ 第一种:Synchronized 和 wait\notifyAll {
@linkplain com.run.threads.synch.SynchProducerAndConsumerDemo}
* ▴ 第二种:ReentrantLock 和 Condition
*
*
* ??描述一个CountDownLatch 类{
@linkplain java.util.concurrent.CountDownLatch} 的作用和使用场景?
* ➢ CountDownLatch 是一个同步辅助类,又称为闭锁.在完成一组正在其它线程中执行的操作之前,
* -它运行一个或多个线程一直等待.
* 以下的应用是可以使用闭锁来实现的:
* 1、闭锁可以延迟线程的进度,直到其到达终止状态.
* 2、闭锁可以用来确保,某些活动直到,其他活动都完成才继续执行.
* 3、确保,某个计算,在其需要的所有资源,都被初始化之后,才继续执行.
* 4、确保,某个服务,在其服务在其,依赖的所有其他服务,都已经启动之后才启动.
* 5、等待,直到某个操作所有参与者,都准备就绪,再继续执行。
* --典型类似倒计时,功能。{
@linkplain com.run.threads.synch.SynchCountDownLatchDemo}
*
* ??描述一个CyclicBarrier 类{
@linkplain java.util.concurrent.CyclicBarrier} 的作用和使用场景?
* ◇ CyclicBarrier(循环栏):CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。
* ◇ CyclicBarrier 和 CountDownLatch 非常类似,它也可以实现线程间的技术等待,但是它但功能更加复杂和强大。
* ◇ 可以让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所以被屏障拦截的线程才会继续干活。
* ▲ 场景:公司组织周末聚餐吃饭,首先员工们(线程),各自从家里到聚餐地点,全部到齐之后,才开始一起吃东西(同步点)。
* --假如人员没有到齐(阻塞),到的人只能够等待,直到所有人都到齐之后才可以吃饭。
*
* ??描述以下 Semaphore类{
@linkplain java.util.concurrent.Semaphore}的使用场景?
* ◆ Semaphore(信号量)-允许多个线程同时访问资源:
* ◆ ReentrantLock 是一次只允许一个线程访问某个资源、Semaphore 是可以特换它的这个使用场景的。
* ◆ Semaphore 通过 acquire() 获取一个许可(加锁),如果没有就等待,而release() 释放一个许可(释放锁)。
* ◆ 常用来 限流、需要控制连接数的稀缺资源的业务场景下。
* --场景:若一个工厂有5台机器,但是有8个工人,一台机器同时只能被一个工人使用,只有使用完了其它工人才能继续使用。
*
* 对比:ReentrantLock 仅允许一个线程访问某个资源,Semaphore 信号量 ,允许多个线程访问某些资源。
* {
@linkplain com.run.threads.synch.SynchSemaphoreDemo}
点击-源代码地址:CodeChina