一、基础概念
1、线程安全:多线程运行的时候,线程的调度顺序不影响运行的结果。
2、多线程:一个进程有多个线程
3、同步:人为的控制和调度,保证线程安全。
二、线程的状态
1、Blocked,Running,Runnable,Dead,New。
2、正常路线:线程创建时处于New的状态,调用start,进入Runnable状态,获取到CPU进入到Running的状态,运行结束或者是抛出异常退出线程进入Dead状态。
3、进入调用sleep,join,或者是I/O操作进入Blocked状态;sleep结束,Join中断,I/O操作结束,退出Blocked,进入Runnable状态。
4、在synchronized模块内,调用wait进入Blocked状态,其他线程在同一个对象的synchronized 模块内调用对象的notify ,或者是notifyAll进入Runnable状态。
5、调用yield方法。从running进入Runnable状态,让出CPU。
6、interrupte方法,并不是立刻将线程停止。而是将线程中断标识位置为true。
正在运行中的线程一般不会去检测中断标识位,只有wait状态、sleep状态和join状态会检测。被打断的线程会抛出InterruptedException。
三、每个对象都有的方法
1、synchronized, wait, notify 是任何对象都具有的同步工具。
2、每一个持有对象的线程都需要获取对象锁的占有权。调用wait方法,release对象锁,调用notify或者是notify也释放了对象锁。让别的线程去获取对象锁。
3、Java中的每个对象都有一个监视器,来监测并发代码的重入。在非多线程编码时该监视器不发挥作用,反之如果在synchronized 范围内,监视器发挥作用。
wait/notify必须存在于synchronized块中。并且,这三个关键字针对的是同一个监视器(某对象的监视器)。这意味着wait之后,其他线程可以进入同步块执行。
4、volatile:在处理数据时,线程会把值从主存load到本地栈,完成操作后再save回去(volatile关键词的作用:每次针对该变量的操作都激发一次load and save)。
四、内存模型
1、多线程的内存:主内存,线程栈。栈是线程级别的内存。方法的执行过程其实是方法栈在虚拟机栈中从入栈到出栈的过程。
2、方法的执行中都是有自己的方法栈,所以每个方法在线程中执行都是方法栈在线程栈中进入栈,退出栈的过程。
五、基本线程类
1、Thread类
2、Runnable接口
3、Callable接口,实际上是Future模式,分为isDone和get,分别对应无阻赛和阻塞两种。通过Future对象获取返回值和状态。返回值类型和泛型传入的类型一致。
六、Future接口
1、能够中断执行中的任务,判断任务是否执行完成,获取任务执行完成后额结果。
2、FutureTask,实际上实现了Runnable和Future两个接口。
七、高级多线程控制类
1、ThreadLocal:保存线程的独立变量。每一个线程都有一份数据。以内存换运行速度。实际实现就是一个Map。TreadLocalMap,以线程本身为key,以以目标为value。
2、原子类:AtomicInteger、AtomicBoolean……等。主要方法:compareAndSet。与前者比较相同,将第二个参数传给变量。
3、AtomicReference,也许对象会出现,属性丢失的情况,即oldObject == current,但是oldObject.getPropertyA != current.getPropertyA。
这时候,AtomicStampedReference就派上用场了。这也是一个很常用的思路,即加上版本号。AI:怎么使用:?????
4、Lock类:ReadLock,WriteLock,ReentrantLock
八、ReentrantLock(实现的核心是AbstractQueuedSynchronizer,这个的核心又是CAS)
1、Unsafe:java自身无法直接访问底层的操作系统,一般都是通过本地方法调用。但是java开了一个后门:unsafe,提供了硬件级别的原子操作。
2、CAS:比较并交换。java.util.concurrent包全完建立在CAS之上,没有CAS也就没有此包,
CAS有三个操作数:内存值,旧的预期值,要操作的值。当前仅当内存值和旧的预期值相同时,将内存值修改为要操作的值,并且返回true,否则什么都不做,返回false。
3、一个Lock里面可以创建多个Condition实例,实现多路通知
4、notify()方法进行通知时,被通知的线程时Java虚拟机随机选择的,但是ReentrantLock结合Condition可以实现有选择性地通知,这是非常重要的
九、AbstractQueuedSynchronizer(实现的基础是CAS)
1、提供了一个基于FIFO队列,可以用于构建锁或者其他相关的同步装置的基础框架。用一个int来表示状态,state就是这个int。
2、使用的方法是继承,子类管理state的状态的方式就是通过acquire或者release方法。
3、因为多线程环境对状态的改变需要实现原子性(通过CAS来保证),子类对状态的操作需要用到这三个方法。
1) AbstractQueuedSynchronizer.getState()
2) AbstractQueuedSynchronizer.setState(int)
3) AbstractQueuedSynchronizer.compareAndSetState(int, int)
具体可以重写的方法有tryAcquire(int arg),tryRelease(int arg) ,tryAcquireShared(int arg),tryReleaseShared(int arg),isHeldExclusively()。
4、同步器是了AbstractQueuedSynchronizer的子类,作为内部类放在锁中,锁实际上是提供了一种API,供用户使用。
5、真正对线程和资源的控制都是依托同步器这个内部类来实现的,是否可以获取到资源还是排队等待实际上也是锁中的同步器做主。
6、该同步器即可以作为排他模式也可以作为共享模式,当它被定义为一个排他模式时,其他线程对其的获取就被阻止,而共享模式对于多个线程获取都可以成功。
7、因为同步器依赖的是一个FIFO队列。队列中的node:每个线程对同步器的访问都是其中的一个node。同步器中有三个成员变量,head,tail,state
Node {int waitStatus;Node prev;Node next;Node nextWaiter;Thread thread;},
1)int waitStatus 节点状态
1)CANCELLED,值为1,表示当前的线程被取消;
2)SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
3)CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;
4)PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
5)值为0,表示当前节点在sync队列中,等待着获取锁。
2)Node prev 前驱节点 如果当前节点被取消,需要前驱节点和后继节点完成连接
3)Node next 后继节点
4)Node nextWaiter
5)Thread thread 当前线程
8、锁资源的流动是由head向tail。
9、如果获取不到锁,封装成一个node,加入到sync队列。加在尾部的逻辑是:
1)如果尾节点存在(Sync队列已经初始化):
对于一个节点需要做的是将当节点前驱节点指向尾节点(current对于一个节点需要做的是将当节点前驱节点指向尾节点(current.prev = tail),尾节点指向它(tail = current),
原有的尾节点的后继节点指向它(t.next = current)而这些操作要求是原子的。上面的操作是利用尾节点的设置来保证的,也就是compareAndSetTail(pre,node)来完成的,
如果取出来的尾node和当前的tail相同,则将tail设置为新加入的node。这样只需要执行一次,与队列长度无关。复杂度O(1)
2)如果尾节点不存在(Sync队列未初始化):
(1)原子化的分配一个头节点,并将尾节点指向头节点,这一步是初始化,head和tail是一致的;
(2)然后是重复在addWaiter中做的工作,但是在一个while(true)的循环中,直到当前节点入队为止
10、进入sync队列之后,接下来就是要进行锁的获取,或者说是访问控制了,只有一个线程能够在同一时刻继续的运行,而其他的进入等待状态。
11、而每个线程都是一个独立的个体,它们自省的观察,当条件满足的时候(自己的前驱是头结点并且原子性的获取了状态),那么这个线程能够继续运行,否则挂起线程。所以说是先进先出。
12、acquire方法(写在lock里面):
1)状态的维护;
在锁定时,需要维护一个状态(state),而对状态的操作是原子和非阻塞的,compareAndSet确保原子性的修改。
2)状态的获取;
一旦成功的修改了状态,当前节点,就被设置为头节点。
3)sync队列的维护。
获取锁资源不成功的时候,进入睡眠状态,停止线程调度器对当前节点线程的调度。只会主动去轮训一次,后续都会等着前置节点的通知,从睡眠中恢复过来。
13、release方法(unlock里面)
1)尝试释放状态;tryRelease能够保证原子化的将状态设置回去,当然需要使用compareAndSet来保证。
2)唤醒当前节点的后继节点所包含的线程。后续节点优先唤醒,如果后继节点被取消了,从尾部开始往前查找一个。
14、acquireInterruptibly
1)检测当前线程是否被中断:判断当前线程的中断标志位,如果已经被中断了,那么直接抛出异常并将中断标志位设置为false。
2)尝试获取状态;调用tryAcquire获取状态,如果顺利会获取成功并返回。
3)构造节点并加入sync队列;获取状态失败后,将当前线程引用构造为节点并加入到sync队列中。
4)中断检测,在每次被唤醒时,进行中断检测,如果发现当前线程被中断,那么抛出InterruptedException并退出循环。
5)已经开始的线程不会受这个中断影响。
15、doAcquireNanos
1) 加入sync队列;将当前线程构造成为节点Node加入到sync队列中。
2) 条件满足直接返回;退出条件判断,如果前驱节点是头结点并且成功获取到状态,那么设置自己为头结点并退出,返回true,也就是在指定的nanosTimeout之前获取了锁。
3) 获取状态失败休眠一段时间;通过LockSupport.unpark来指定当前线程休眠一段时间。
4)计算再次休眠的时间;唤醒后的线程,计算仍需要休眠的时间,该时间表示为nanosTimeout = 原有nanosTimeout – now(当前时间)+ lastTime(睡眠之前记录的时间)。其中now – lastTime表示这次睡眠所持续的时间。
5)休眠时间的判定。唤醒后的线程,计算仍需要休眠的时间,并无阻塞的尝试再获取状态,如果失败后查看其nanosTimeout是否大于0,如果小于0,那么返回完全超时,没有获取到锁。
如果nanosTimeout小于等于1000L纳秒,则进入快速的自旋过程。那么快速自旋会造成处理器资源紧张吗?结果是不会,经过测算,开销看起来很小,几乎微乎其微。
Doug Lea应该测算了在线程调度器上的切换造成的额外开销,因此在短时1000纳秒内就让当前线程进入快速自旋状态,如果这时再休眠相反会让nanosTimeout的获取时间变得更加不精确。
16、acquireShared 和acquire类似。
1. 尝试获取共享状态;调用tryAcquireShared来获取共享状态,该方法是非阻塞的,如果获取成功则立刻返回,也就表示获取共享锁成功。
2. 获取失败进入sync队列;在获取共享状态失败后,当前时刻有可能是独占锁被其他线程所把持,那么将当前线程构造成为节点(共享模式)加入到sync队列中。
3. 循环内判断退出队列条件;如果当前节点的前驱节点是头结点并且获取共享状态成功,这里和独占锁acquire的退出队列条件类似。
4. 获取共享状态成功;在退出队列的条件上,和独占锁之间的主要区别在于获取共享状态成功之后的行为,而如果共享状态获取成功之后会判断后继节点是否是共享模式,
如果是共享模式,那么就直接对其进行唤醒操作,也就是同时激发多个线程并发的运行。
5. 获取共享状态失败。通过使用LockSupport将当前线程从线程调度器上摘下,进入休眠状态。
17、releaseShared
唤醒后续节点。可能唤醒多个。
分享到:
相关推荐
本文将围绕“Java多线程高并发相关资料收集”这一主题,详细探讨这两个领域的核心知识点。 首先,多线程是指在单个程序中同时执行多个线程。Java提供了一个强大的多线程支持,允许开发者创建、管理和控制多个执行...
这五本书籍,包括《JAVA并发编程 核心方法与框架》、《Java并发编程:设计原则与模式(第二版)》、《Java并发编程实战(中文版)》、《Java多线程编程核心技术_完整版》以及《Java多线程编程实战指南 设计模式篇》...
本文将围绕“Java面试多线程高并发相关回家技巧”这一主题,深入探讨相关概念、原理以及面试中可能遇到的问题,帮助你做好充分准备。 一、线程基础 1. **线程的创建**:Java提供了两种创建线程的方式,一是通过实现...
使用`show parameter`命令可以查看与并发相关的系统参数。例如,执行`show parameter processes;`命令,可以显示`PROCESSES`参数的值,该参数定义了数据库实例允许的最大并发进程数,其中包括了所有类型的会话(如...
资源名称:Java多线程并发相关资料汇总 资源目录: 【】JavaConcurrencyinPractice 【】JavaThreads(3rdEdition) 【】JAVA多线程并发 【】Java多线程并发访问解决方案 【】java多...
并发相关知识点思维导图整理,适合java开发人员
java高并发相关知识,threadLocal,分布式锁,java各种锁等
- **JDK1.3**: 引入了J2SE、J2EE和J2ME三个发展方向,同时JVM首次内置了JIT编译器,虽然此阶段还未引入并发相关的API,但是为后续的并发优化打下了基础。 - **JDK1.4**: 增加了许多新特性,如正则表达式、异常链、...
在IT行业中,高并发是一个关键领域,特别是在互联网和大数据时代,系统能否处理大量并发请求,直接决定了服务的稳定性和用户体验。本实践主要关注如何设计和优化高并发系统,确保其在大规模用户访问下仍能高效运行。...
总结,理解和掌握这些高并发相关的概念和技巧对于构建高性能、可扩展的系统至关重要。从同步异步的选择,到并发并行的理解,再到线程管理和无锁编程,每个环节都对系统的效率和稳定性产生深远影响。在实际应用中,...
- 数据库优化:并发问题往往与数据库的性能紧密相关,可能需要进行数据库索引优化、读写分离、缓存策略调整等,以提高整体性能。 - 监控与调优:定期监控系统性能,通过日志分析和性能监控工具找出瓶颈,进行持续...
在IT领域,多线程和并发是至关重要的概念,尤其在现代高性能计算和分布式系统中。多线程允许一个程序同时执行多个不同的任务,而并发则意味着系统可以在同一时间处理多个请求或操作,提升了资源利用率和系统响应速度...
学会高并发处理思路与手段,让跳槽面试...无论面试还是实际开发,几乎都会涉及并发相关知识及高并发相关场景处理,如果你想系统的学习一下并发编程 并了解一下实际的高并发场景及应对方案,那这门课就是为你准备的。
Java高并发编程是Java开发中的重要领域,尤其在大规模分布式系统和互联网应用中,对系统的高并发处理能力有着极高的要求。以下将详细介绍这些关键知识点: 1. **线程** - **继承Thread类**:创建线程的一种方式是...
该压缩包里含有两本pdf书籍:effictive java第二版和java并发实战,希望能对大家提升技术有所帮助。
下面,我们将深入探讨Java并发相关的知识体系。 一、并发基础 并发是指多个任务在同一时间间隔内执行,而并行则意味着这些任务是真正的在同一时刻执行。Java中的并发主要通过多线程实现,线程是操作系统分配CPU资源...
书中会首先介绍Java并发编程的基础知识,包括线程的创建和运行,同步机制的基本用法,以及Java内存模型的相关概念。随着章节的深入,作者可能会更深入地讲解Java提供的并发工具,例如锁、原子变量、线程池、以及并发...
此外,书中可能还会涉及其他并发编程相关的高级话题,比如原子变量(`AtomicInteger`, `AtomicReference`等)、并发工具类(如`CountDownLatch`, `CyclicBarrier`, `Semaphore`)以及Fork/Join框架。这些工具可以...
本文档主要系统性的总结和阐述了与Java并发相关的知识点
2. **同步机制**:Java中的同步机制包括`synchronized`关键字、`wait()`, `notify()`和`notifyAll()`方法,以及`Lock`接口和相关的实现,如`ReentrantLock`。这些机制用于控制对共享资源的访问,防止数据不一致和竞...