JUC复习指南:
JUC有哪些知识点?
-
什么是Juc
-
Lock接口
-
线程间通信
-
集合的线程安全问题
-
多线程锁
-
Callable接口
-
JUC三大辅助类 CountDownLatch CyclicBarrier Semaphore
-
读写锁 ReetrantReadWriteLOck
-
阻塞队列
-
ThreadPool线程池
-
Fork/join
-
CompletableFuture
-
中断机制
-
LockSupport
-
JMM
-
volatile
-
CAS
-
原子类
-
ThreadLocal
-
synchronized之锁升级
-
AQS
-
StampedLock
Q:什么是Juc
是 java.util .concurrent 工具包的简称,这是一个处理线程的工具包,JDK 1.5 开始出现的
Q:线程的状态
public enum State {
/**
* Thread state for a thread which has not yet started.
新建态,线程尚未执行
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
可运行态,可以理解为操作系统本地线程的就绪态
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
阻塞态,一个等待获得同步监视器,准备进入同步带模块的线程
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
等待态,必须满足上述方法调用逻辑的情况下才能成为此状态
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
超时等待态,必须满足上述方法调用逻辑的情况下才能成为此状态
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
终结态,线程完成执行任务。
*/
TERMINATED;
}
和操作系统里对进程、线程状态的描述还是有区别的
操作系统中 进程五态和线程的状态是类似的主要关注的就是 就绪 阻塞 执行。
Q :wait/sleep 的区别
(1)sleep 是 Thread 的静态方法,wait 是 Object 的方法,任何对象实例都 能调用。
(2)sleep 不会释放锁,它也不需要占用锁。wait 会释放锁,但调用它的前提 是当前线程占有锁(即代码要在 synchronized 中)。
(3)它们都可以被 interrupted 方法中断。
Q:多线程并发问题的异步性问题
请注意,我问题中的【异步性】 是操作系统的特性,它指的是
操作系统对于进程/作业的调度顺序在业务层面来看是不确定的(并不是从具体调度算法的角度看),在并发执行任务的场景下,就可能出现这些现象:
-
一个任务多次被执行,但是其他任务始终没用被执行
-
理想希望任务按照一定的顺序执行,但实际执行中任务是乱序的。
在操作系统中,解决上述问题的办法,就是同步与互斥机制。
同步互斥机制在实现原理上基本就是硬件层面和软件层面,具体实现有原语、互斥锁,信号量,管程等机制。
同步是一种直接制约关系,它强调的是对进程/线程的工作顺序、传递信息进行控制,协调。
互斥是间接制约关系 它表现的是多个进程/线程对临界资源的访问关系,一者访问,其余等待。
在java中,对于并发执行产生的异步性问题,也有它自己的具体实现方案。除此之外,Java所说的异步处理任务,和操作系统异步性的概念是不一样的。
java的异步处理任务,是通过Java的某些API或外部中间件,使得的多个任务的执行互相不再发生干涉,简而言之,就是:
你干你的事,我干我的事,你干完给我一个结果通知,而不是让我干等你的执行结果,再开始工作。
但是请注意一点:不管是开发上的异步处理,同步、互斥,他们并不是在任何场景下都能用的,要根据具体业务场景来具体设计。
比如生产者-消费者模型下,缓冲区(容量是1)内必须要写满数据,消费者才能执行,缓冲区内为空,生产者才能生产。二者不可以同时访问缓冲区。
这个模型就是典型的同步+互斥。并不适合用异步处理。
那怎么用异步来改善这个模型呢?
假设当前的生产者执行的速率很快,消费者的执行速率很慢,此时就存在一个速度差。
为了缓解这个问题,我们可与模拟计算机网络的流量控制的思想,为消费者创建接收缓存队列(容量大于5),
假设生产速度是消费速度的5倍,缓冲区内很快塞满了,消费者一个一个的去消费,就可能让生产者一直等待。但是有了接收缓存队列后,生产者可以保持运行,也不必等待消费者必须把缓冲区的内容消费了,生产者可以生产到缓存队列满了再停等,减少了系统的停等时间。
进一步的,可以设计一个滑动窗口算法,让二者的发送和接收的速率达到一个平衡点。
通过一个缓存队列,把生产者和消费者的直接制约关系破坏,打破同步,形成异步关系,可以有效提高CPU的利用率,减少空等时间,提高了系统吞吐量。
java关于异步的一个API CompletableFuture,在后面再详细介绍。
Q:管程
管程实际上操作系统的一种进程同步工具。管程可以保证进程互斥,降低由于信号量pv操作不当引发的死锁问题
同一时间内,只有一个进程可以在管程内活动。
管程有自己的共享数据结构和具体过程
共享数据结构抽象的标识系统的共享资源,而对该数据结构执行的一组操作,可以定义为一组过程。进程对于共享资源的申请、释放等操作,都是通过这些过程来实现的。这组过程可以根据资源共享的情况,或接收或阻塞进程的访问,确保每次仅仅有一个进程使用共享资源,这样就实现对共享资源的统一管理,实现互斥。
简单来说,管程的定义结构如下:
-
管程的名称
-
局部于管程内的共享数据结构
-
对该数据结构进行操作的一组过程
-
对局部于管程内的数据结构的初始值语句
除了上面对管程的定义外,管程还具有条件变量
当一个进程进入管程后,其他所有尝试获取管程的进程会被阻塞,被加入该管程的阻塞队列。
直到该进程用完管程,释放资源,并且唤醒处于阻塞队列中的进程。
monitor Demo{
共享数据结构 S;
condition x;
init_code(){...}
take_away(){
if(S<0) x.wait();
//若资源足够,则不用阻塞,直接进行分配
}
give_back(){
归还资源 进一步处理;
if(阻塞队列里有进程等待) x.signal() //唤醒一个进程
}
}
现在我们回到java的管程——synchronized 同步监视器
JVM 中同步是基于进入和退出管程(monitor)对象实现的,每个对象都会有一个管程 (monitor)对象,管程(monitor)会随着 java 对象一同创建和销毁
执行线程首先要持有管程对象,然后才能执行方法,当方法完成之后会释放管程,方 法在执行时候会持有管程,其他线程无法再获取同一个管程.
我们可以看到,JVM中的管程是线程粒度的,毕竟,一个JVM只对应一个工作进程。