一 线程生命周期
二 创建线程的几种方式
public class T implements Runnable {
public void m1() {
System.out.println("m1 start");
}
public static void main(String[] args) {
T t = new T();
// 方式一:JDK1.8使用Lambda表达式
new Thread(t::m1, "t1").start();
new Thread(() -> {
System.out.println("()-> start");
}).start();
// 方式二:实现Runnable接口
new Thread(t).start();
// 方式三:集成Thread类
T2 t2 = new T2();
t2.start();
}
@Override
public void run() {
System.out.println("run start");
}
// 继承Thread类
public static class T2 extends Thread {
@Override
public void run() {
System.out.println("T2 extends Thread{ start");
}
}
}
三 Synchronized 可重复锁
- synchronized:是重量级锁,OS底层实现。Synchronized(Object)锁对象不能使用String、Integer等基本数据类型对象。
- markword:偏向锁记录线程ID,当同一线程再次执行时,即可获得执行权限,发生线程争用时,升级为自旋锁。
- 自旋锁:达到自旋10次以上时,再次锁升级,升级到synchronized重量级锁。
- 锁只能升级,不能降级。
- 自旋锁使用场景:加锁代码块执行时间少并且线程少。
- 系统锁:加锁代码块执行时间长,线程数较多。
四 volatile
- 保证变量在线程间的可见性
- 禁止指令重排序
五 CAS 乐观锁(自旋)
cas 是 Compare And Swap 比较并替换的简称,其原理是cas(V 当前内存中的值,Expected 期望值,NewValue 变更后值)在并发量不大的情况下,可以代替Synchronized提供系统性能,cas的本质是自旋锁,当期望值 E 与 内存中值 V 相同时,NewValue 替换当前内存中值 V,当前内存值为 NewValue。如果期望值 E 与内存中值 V 不一致时,放弃当前操作。通过自旋原理,线程尝试更新。虽然CAS可以带来系统上性能的提升,但是也存在ABA问题。
六 ABA问题
ABA问题主要是CAS操作过程时,存在多个线程同时访问同一资源,线程A 首先将内存中的值变更为B,线程A又经过一系列操作,将内存中的值变更为A,线程B经过一段阻塞后,重新获得CPU执行,线程B发现内存值仍然是A,线程B将内存值又更新为B,由于多线程存在CPU资源占用,导致ABA问题的存在,为了解决ABA问题,我们引入版本号的概念避免了ABA问题的发生,通俗的一句话解释就是:你大爷还是你大爷,你大妈不再是你大妈了!!!
七 JDK中常见锁的应用
1、CyclicBarrier
CyclicBarrier 栅栏,当一组线程全部完成任务时,触发的线程。举例:由于疫情的影响,线下麻将馆全部关闭,大家纷纷通过手机的方式进行娱乐,这时,一个人事先开好了一个游戏房间,等待其它三位玩家进入,当所有玩家都进入房间后,游戏开始。游戏房间类似于CyclicBarrier,4个玩家同比4个线程,只有4个线程都完成后,游戏才能正式开始。
public class TestCyclicBarrier {
public static void main(String[] args) {
// 数量达到4,线程执行
CyclicBarrier barrier = new CyclicBarrier(4, new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName() + "房间人已满,开始对战");
}
});
for (int i = 0; i < 20; i++) {
new Thread(() -> {
try {
barrier.await();
// System.out.println(Thread.currentThread().getName()+"已进入房间");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}).start();
}
}
}
2、Phaser
phaser 阶段器,比 CyclicBarrier CountDownLanuc更加灵活,在我们现实生活中类似于一个班级的模拟考试,只有当学生全部到位时,才能开始第一门课程的考试,4门课程全部考试结束后,本次模拟考试正式结束。也可以理解成一个通过游戏,只有把每一关的怪兽全部消灭掉,才能进入下一关,直到完成通关。
public class TestPhaser {
private static MyPash phaser = new MyPash();
public static void main(String[] args) {
phaser.bulkRegister(4);
new Thread(new Person("ADC")).start();
new Thread(new Person("盖伦")).start();
new Thread(new Person("法师")).start();
new Thread(new Person("皇子")).start();
}
static class Person implements Runnable {
private String name;
public Person(String name) {
this.name = name;
}
public void arrived() {
try {
System.out.println(name + " 已进入游戏房间,即将开始第一关");
phaser.arriveAndAwaitAdvance();
} finally {
}
}
public void second() {
try {
System.out.println(name + " 已进入游戏第二关,common on!!!");
phaser.arriveAndAwaitAdvance();
} finally {
}
}
public void over() {
try {
if (name.equals("法师") || name.equals("ADC")) {
System.out.println(name + "恭喜您通关");
phaser.arriveAndAwaitAdvance();
} else {
System.out.println(name + ",已被 Npc Kill,等待其它玩家!!");
phaser.arriveAndDeregister();
}
} finally {
}
}
@Override
public void run() {
arrived();
second();
over();
}
}
static class MyPash extends Phaser {
@Override
protected boolean onAdvance(int phase, int registeredParties) {
switch (phase) {
case 0:
System.out.println("游戏开始,开始第一关!!!,剩余玩家 " + registeredParties);
return false;
case 1:
System.out.println("第二关已通过,继续努力下一关!!!,剩余玩家" + registeredParties);
return false;
case 2:
System.out.println("游戏结束,恭喜通关,剩余玩家" + registeredParties);
return true;
default:
return true;
}
}
}
}
3、Semaphore
业务场景限流,可以联想到现实生活中高速公路上的收费站,例如有一条单向8车道的高速公路,在某一处收费站只有三个收费窗口,当汽车想要通过收费站时,一种情况是大家抢着通过,谁先到达谁优先通过,还有一种是公平的方案,大家都排队出去。这里每一辆车可以理解为一个线程,收费窗口为某一资源。semaphore 在使用时可以设置是否公平的方式fair 为 true
public class TestSemaphore {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(1);
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 到达收费窗口,请扫码缴费");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + " 缴费完成");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
// TODO: handle finally clause
semaphore.release();
}
}, "宝马X").start();
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 到达收费窗口,请扫码缴费");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + " 缴费完成");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
semaphore.release();
}
}, "奥迪A8").start();
}
}
4、Exchanger
exchanger交换器,从语意上我们也能看出,交换只存在两者之间,不能有第三者,所有exchanger只有两个线程间的数据交换。
public class TestExchanger {
static Exchanger<String> exchanger = new Exchanger<String>();
public static void main(String[] args) {
new Thread(() -> {
String s = "我是A";
try {
s = exchanger.exchange(s);
System.out.println(Thread.currentThread().getName() + "," + s);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程A").start();
new Thread(() -> {
String s = "我是B";
try {
s = exchanger.exchange(s);
System.out.println(Thread.currentThread().getName() + "," + s);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程B").start();
}
}
8、ReentrantLock
ReentrantLock 可循环锁
package com.test;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestReentrantLock {
Lock lock = new ReentrantLock();
public void m1() {
try {
// 获得锁
lock.lock();
System.out.println(Thread.currentThread().getName() + "m1 获得锁……");
m2();
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
// 释放锁
System.out.println(Thread.currentThread().getName() + "m1 释放锁……");
lock.unlock();
}
}
public void m2() {
try {
// 获得锁
System.out.println(Thread.currentThread().getName() + "m2 获得锁……");
lock.lock();
} finally {
// 释放锁
System.out.println(Thread.currentThread().getName() + "m2 释放锁……");
lock.unlock();
}
}
public static void main(String[] args) {
TestReentrantLock reentrantLock = new TestReentrantLock();
new Thread(reentrantLock::m1, "张三").start();
new Thread(reentrantLock::m2, "李四").start();
}
}
9、ReadWriteLock 读写锁(共享锁、排他锁)