1. 大纲
- lock锁
- 生产者 消费者
- 锁的现象
- 线程类 不安全
- Callable
- CountDownLatch
- CyclicBarrier
- Semaphore
- 读写锁
- 阻塞队列
- 线程池
- 四大函数式接口
- Stream流式计算
- 分支合并
- 异步回调
- JMM
- volatile
- 单例模式
- CAS
- 原子引入
- 可重入锁
- 公平锁 非公平锁
- 自旋锁
- 死锁
Latch
英
/lætʃ/
美
/lætʃ/
全球发音
简明 牛津 新牛津 韦氏 柯林斯 例句 百科
n.
门闩,窗闩;<英>碰锁,碰簧锁;(电子)门闩线路,锁存器
v.
闩上(门),用弹簧锁锁住(门等);(电子)(装置)状态恒定;占有,抓住
barrier
英
/ˈbæriə(r)/
美
/ˈbæriər/
n.
障碍,壁垒;障碍物,关卡;分界线,屏障;大关,界限
Semaphore
英
/ˈseməfɔː(r)/
美
/ˈseməfɔːr/
(计)信号量
signal
信号,暗号;标志,预示;(尤指铁路和公路上的)指示灯,信号灯;(无线电、电视等的)讯号,信号;起因,导火线;(桥牌)信号;(传递的)信息
v.
发信号,示意;标志,预示;告知,表示;(车辆转方向时)打灯示意
adj.
重大的,显要的;作为信号的
volatile
adj.
易变的,动荡不定的,反复无常的;(情绪)易变的,易怒的,突然发作的;(液体或固体)易挥发的,易气化的;(计算机内存)易失的
Cyclic
英
/ˈsaɪklɪk/
美
/ˈsaɪklɪk/
全球发音
简明 牛津 新牛津 韦氏 柯林斯 例句 百科
adj.
环的;循环的;周期的
cycl
pref.
圆形的;与自行车有关的;环状的(用于元音前,同 cyclo-)
cycle
n.
自行车,摩托车;循环,周期;组诗,组歌;整套,系列;自行车骑行;一段时间
v.
骑自行车;循环
bicycle
n.
单车,自行车
v.
骑自行车
所属的三个包
java.util.concurrent
- 实用程序类通常在并发编程中很有用。
- Callable
java.util.concurrent.atomic
- 一个小型工具包,支持单个变量上的无锁线程安全编程。
java.util.concurrent.locks
- 接口和类提供了一个框架,用于锁定和等待与内置同步和监视器不同的条件。
- Lock类
java.util.function
- 功能界面提供了lambda表达式和方法引用的目标类型。
concurrent下
util.concurrent
-
Blocking Queue
- Deque
-
Callable
-
Completable Future
-
ConcurrentHashMap
- LinkedQueue
-
CopyOnWriteArrayList
- ArraySet
-
CountDownLatch
-
CyclicBarrier
-
DelayQueue
-
Executor
- Executors
-
Future
- FutureTask
-
LinkedBlockingDeque
- Queue
-
Runnable Future
-
Scheduled Future
- Scheduled Executor Serive
- Schedule Thread Pool Executor
-
Semaphore
-
ThreadPoolExecutor
-
TimeUnit
deque
英
/dek/
美
/dɛk/
n.
双端队列;双队列
atomic下
concurrent.atomic
- Automic Boolean
- Integer
- IntegerArray
- Long
- LongArray
- Reference
- DoubleAccumulator
- Double Addre
- Long Accumulator
- Long Adder
- Stringped 64
locks下,三大接口
concurrent.locks
-
AbstractOwnableSynchronizer
-
AbstractQueuedLongSynchronizer
-
AbstractQueuedSynchronizer
-
Condition 接口
-
Lock 接口
- LockSupport
-
ReadWriteLock 接口
- ReentrantLock
- ReenTrantReadWriteLock
- StampedLock
2. 线程和进程
- Call able
- Runnable
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
java默认有两个线程:
-
main ,GC
-
线程:开了一个进程Typora,写字,自动保存(线程负责)
start()源码
- java真的可以开启线程吗?不可以
- 底层是:本地方法,调用底层c++,java运行在虚拟机之上,无法直接操作硬件,由c++开启多线程
new Thread(new MyThread(lockA, lockB), "T1").start();
public synchronized void start() {
group.add(this);
boolean started = false;
start0();
started = true;
if (!started) {
group.threadStartFailed(this);
}
}
//本地方法
private native void start0();
3. 线程回顾
//获取cpu的核数
//cpu密集型,io密集型
System.out.println(Runtime.getRuntime().availableProcessors());
processors
/prɔ:'sesəz/
(计)处理器,处理机;加工者,加工物(processor 的复数)
process
n.
步骤,程序;(自然或偶然的)变化过程;(为达到某目标的)过程,进程;制作方法,加工方法;<法律>传票;(生,剖)端突,突起
adj.
(印刷)三原色的,三色版的; 经过特殊加工的;照相板的
v.
(用化学物品或机器)处理,加工;审核,受理(正式文件或请求);(计算机)处理(数据);冲洗(照片);加工(食品);<正式>列队行进;把(头发)弄成直发
线程6状态
public enum State {
//就绪
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待,死等
WAITING,
//超时等待
TIMED_WAITING,
//终止 ter mi na ted
TERMINATED;
}
wait与sleep
- 来自不同的类
wait=》Object
sleep=》Thread - wait释放锁,sleep抱着锁睡觉
- 使用的范围是不同的。wait必须在同步代码快中,sleep可以在任何地方睡觉
- wait不需要捕获异常(这个错了,都要捕获异常),sleep需要捕获异常(可能发生超时等待)
import java.util.concurrent.TimeUnit;
try {
//更好的睡眠
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
Integer i=new Integer(1);
try {
i.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
4. 卖票的代码
/**
* 真正的多线程开发,公司中的开发,降低耦合性
* 线程就是一个单独的资源类,没有任何附属的操作!
* 1、 属性、方法
*/
public class SaleTicketDemo01 {
public static void main(String[] args) {
// 并发:多线程操作同一个资源类, 把资源类丢入线程
Ticket ticket = new Ticket();
// @FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{ 代码 }
new Thread(()->{
for (int i = 1; i < 40 ; i++) {
ticket.sale();
}
},"A").start();
}
}
// 资源类 OOP
class Ticket {
// 属性、方法
private int number = 30;
// 卖票的方式
// synchronized 本质: 队列,锁
public synchronized void sale(){
if (number>0){
System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
}
}
}
5. Lock锁
-
java.util.concurrent.locks
- Condition
- Lock
- ReadWriteLock 读写锁
-
所有已知实现类:
-
ReentrantLock
-
ReentrantReadWriteLock.ReadLock
-
ReentrantReadWriteLock.WriteLock
-
entrant
英
/ˈentrənt/
美
/ˈentrənt/
全球发音
简明 牛津 新牛津 韦氏 柯林斯 例句 百科
n.
进入者;新会员;参加竞赛者;新工作者
ReentrantLock
- 源码
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
fail
v.
失败;未能做到,未能做;辜负,使失望;不及格,未能通过;出故障,失灵;衰弱,衰退;破产,倒闭;歉收;不下雨,下雨少;忘记
n.
不及格;<非正式>过失,失败
fair
adj.
公平的;合理的,公正的;相当好的;(数量、大小)相当大的;浅色的,(肤色)白皙的,(头发)金色的;晴朗的;顺风的;普通的;美丽的;界内的;<旧>(手段,方法)温和的,非暴力的;<旧>(言语,谈话,许诺)花言巧语的
adv.
公平地,公正地;非常;正面地,直接地;清楚地
公平锁:十分公平:可以先来后到
非公平锁:十分不公平:可以插队 (默认)
使用
// Lock三部曲
// 1、 new ReentrantLock();
// 2、 lock.lock(); // 加锁
// 3、 finally=> lock.unlock(); // 解锁
class Ticket2 {
// 属性、方法
private int number = 30;
Lock lock = new ReentrantLock();
public void sale(){
lock.lock(); // 加锁
try {
// 业务代码
if (number>0){
System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余:"+number);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock(); // 解锁
}
}
}
// 并发:多线程操作同一个资源类, 把资源类丢入线程
Ticket2 ticket = new Ticket2();
new Thread(()->{for (int i = 1; i < 40 ; i++) ticket.sale();},"A").start();
Synchronized 和 Lock
Synchronized 和 Lock 区别
1、Synchronized 内置的Java关键字, Lock 是一个Java接口
2、Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
3、Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁
4、Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下
去;
lock.tryLock()
5、Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以
自己设置);
6、Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码
6. 生产者 消费者
- Synchronized ,wait,notify
传统写法
/**
* 线程之间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒
* 线程交替执行 A B 操作同一个变量 num = 0
* A num+1
* B num-1
*/
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
//然后再写 B线程
}
}
// 判断等待,业务,通知
class Data{ // 数字 资源类
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
while (number!=0){ //0
// 等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他线程,我+1完毕了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
while (number==0){ // 1
// 等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他线程,我-1完毕了
this.notifyAll();
}
}
问题存在,A B C D 4 个线程!
- 把上面的 while改为if,产生虚假唤醒
虚假唤醒
线程也可以唤醒,而不会被通知,中断或超时,即所谓的虚假唤醒 。 虽然这在实践中很少会发生,但应用程序必须通过测试应该使线程被唤醒的条件来防范,并且如果条件不满足则继续等待。 换句话说,等待应该总是出现在循环中,就像这样:
synchronized (obj) {
while (<condition does not hold>)
obj.wait(timeout);
... // Perform action appropriate to condition
}
while (number!=0){ //0
// 等待
this.wait();
}
7. Condition
Condition
因素出Object
监视器方法(wait
,notify
和notifyAll
)成不同的对象,以得到具有多个等待集的每个对象,通过将它们与使用任意的组合的效果Lock
个实现。Lock
替换synchronized
方法和语句的使用,Condition
取代了对象监视器方法的使用。
直接唤醒全部
// 判断等待,业务,通知
class Data2{ // 数字 资源类
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//condition.await(); // 等待
//condition.signalAll(); // 唤醒全部
//+1
public void increment() throws InterruptedException {
lock.lock();
try {
// 业务代码
while (number!=0){ //0
// 等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他线程,我+1完毕了
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//-1
public void decrement() throws InterruptedException {
lock.lock();
try {
while (number==0){ // 1
// 等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他线程,我-1完毕了
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
精准的通知和唤醒线程
class Data3{ // 资源类 Lock
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int number = 1; // 1A 2B 3C
public void printA(){
lock.lock();
try {
// 业务,判断-> 执行-> 通知
while (number!=1){
// 等待
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"=>AAAAAAA");
// 唤醒,唤醒指定的人,B
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
// 业务,判断-> 执行-> 通知
while (number!=2){
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBB");
// 唤醒,唤醒指定的人,c
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
// 业务,判断-> 执行-> 通知
// 业务,判断-> 执行-> 通知
while (number!=3){
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"=>CCCCCCC");
// 唤醒,唤醒指定的人,c
number = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}