要求
六种状态及转换
分别是
其它情况(只需了解)
- 可以用 interrupt() 方法打断等待、有时限等待的线程,让它们恢复为可运行状态
- park,unpark 等方法也可以让线程等待和唤醒
五种状态
五种状态的说法来自于操作系统层面的划分
要求
七大参数
代码说明
day02.TestThreadPoolExecutor 以较为形象的方式演示了线程池的核心组成
要求
一个共同点,三个不同点
共同点
不同点
要求
三个层面
不同点
公平锁
ReentrantLock可重入锁
条件变量
代码说明
- day02.TestReentrantLock 用较为形象的方式演示 ReentrantLock 的内部结构
要求
原子性
可见性
有序性
代码说明
- day02.threadsafe.AddAndSubtract 演示原子性
- day02.threadsafe.ForeverLoop 演示可见性
- 注意:本例经实践检验是编译器优化导致的可见性问题
- day02.threadsafe.Reordering 演示有序性
- 需要打成 jar 包后测试
- 请同时参考视频讲解
要求
对比悲观锁与乐观锁
// –add-opens java.base/jdk.internal.misc=ALL-UNNAMED public class SyncVsCas { static final Unsafe UNSAFE_9 = Unsafe.getUnsafe(); // CAS 操作需要知道要操作的字段的内存地址 static final long BALANCE = UNSAFE_9.objectFieldOffset(Account.class, “balance”); // 在指定的类中查找指定的字段,并返回该字段在内存中的偏移量 Unsafe.getUnsafe().objectFieldOffset(Account.class, “balance”)
static class Account { // 进行 CAS 操作时通常需要加 volatile 修饰变量,保证该字段的可见性和有序性。 volatile int balance = 10; // 余额 }
private static void showResult(Account account, Thread t1, Thread t2) { try { t1.start(); t2.start(); t1.join(); t2.join(); LoggerUtils.get().debug(“{}”, account.balance); } catch (InterruptedException e) { e.printStackTrace(); } }
public static void sync(Account account) { Thread t1 = new Thread(() -> { synchronized (account) { int old = account.balance; int n = old - 5; account.balance = n; } },”t1”);
Thread t2 = new Thread(() -> {
synchronized (account) {
int o = account.balance;
int n = o + 5;
account.balance = n;
}
},"t2");
showResult(account, t1, t2); }
public static void cas(Account account) { Thread t1 = new Thread(() -> { // 乐观锁 while (true) { int o = account.balance; int n = o - 5; // 比较和设置CAS if (UNSAFE_9.compareAndSetInt(account, BALANCE, o, n)) { break; } } },”t1”);
Thread t2 = new Thread(() -> {
while (true) {
int o = account.balance;
int n = o + 5;
if (UNSAFE_9.compareAndSetInt(account, BALANCE, o, n)) {
break;
}
}
},"t2");
showResult(account, t1, t2); }
private static void basicCas(Account account) { while (true) { int o = account.balance; int n = o + 5; if(UNSAFE_9.compareAndSetInt(account, BALANCE, o, n)){ break; } } System.out.println(account.balance); }
public static void main(String[] args) { Account account = new Account(); cas(account); } }
该方法会比较 expectedValue 和当前引用所指向的值,如果相同,则将该引用设置为 newValue。
```java
import java.util.concurrent.atomic.AtomicReference;
public class OptimisticLock {
private AtomicReference<Object> value = new AtomicReference<>(new Object());
public Object getValue() {
return value.get();
}
public boolean update(Object expectedValue, Object newValue) {
return value.compareAndSet(expectedValue, newValue);
}
}
import groovy.lang.GroovyShell;
import java.io.FileReader;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
// -XX:MaxMetaspaceSize=24m
// 模拟不断生成类, 但类无法卸载的情况
public class TestOomTooManyClass {
// static GroovyShell shell = new GroovyShell();
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger();
while (true) {
try (FileReader reader = new FileReader("script")) {
GroovyShell shell = new GroovyShell();
shell.evaluate(reader);
System.out.println(atomicInteger.incrementAndGet());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
monitor监控(线程阻塞状态)
代码说明
- day02.SyncVsCas 演示了分别使用乐观锁和悲观锁解决原子赋值
- 请同时参考视频讲解
要求
更形象的演示,见资料中的 hash-demo.jar,运行需要 jdk14 以上环境,进入 jar 包目录,执行下面命令
java -jar --add-exports java.base/jdk.internal.misc=ALL-UNNAMED hash-demo.jar
Hashtable 对比 ConcurrentHashMap
ConcurrentHashMap 1.7
ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel)
默认的构造函数,默认的扩容因子是0.75,容量是16.
Segment(大数组) + HashEntry(小数组) + 链表
,每个 Segment 对应一把锁,如果多个线程访问不同的 Segment,则不会冲突ConcurrentHashMap 1.8
Node 数组 + 链表或红黑树
,数组的每个头节点作为锁,如果多个线程访问的头节点不同,则不会冲突。首次生成头节点时如果发生竞争,利用 cas(Compare-and-Swap)而非 syncronized,进一步提升性能。要求
作用
原理
每个线程内有一个 ThreadLocalMap 类型的成员变量,用来存储资源对象
ThreadLocalMap 的一些特点
弱引用 key
ThreadLocalMap 中的 key 被设计为弱引用,原因如下
内存释放时机
记录线程池lambda
public class TestThreadPoolExecutor {
public static void main(String[] args) throws InterruptedException {
AtomicInteger c = new AtomicInteger(1);
// 数组阻塞队列
ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
2,
3,
0, // 救急线程的生存时间,生存时间内没有新任务,此线程资源会释放
TimeUnit.MILLISECONDS,
queue,
// ThreadFactory 是一个接口,它提供了一个 newThread 方法,用来创建新的线程。 lambda 表达式实际上就是实现了这个方法。
// 其中 new Thread(r, "myThread" + c.getAndIncrement()) 将runnable r作为参数传入,创建了一个新线程并启动了它。
r -> new Thread(r, "myThread" + c.getAndIncrement()),
new ThreadPoolExecutor.DiscardOldestPolicy());
Thread thread = new Thread("myThread" + c.getAndIncrement());
// Thread(Runnable target, String name)
// 声明具体接口类型 确定lambda表达式所表示的具体抽象方法
Runnable rr = () -> {};
// 错误的写法,传入的参数Runnable r没有起到任何作用。实现该接口时应该使用方法里的固定的参数(Runnable r)实现约定的抽象方法(返回Thread对象)
ThreadFactory threadFactory1 = r -> new Thread(() -> {},"");
// 同样是错误的
ThreadFactory threadFactory11 = r -> new Thread(new Runnable() {
@Override
public void run() {
System.out.println();
}
}, "");
// 正确的写法t2和t22。对于有参数的抽象方法可省略小括号(直接定义参数名r箭头指向->方法体),一行代码返回时可省略大括号{}和里面的return。
ThreadFactory t2 = r -> {System.out.println(true);return new Thread(r, "");};
ThreadFactory t22 = new ThreadFactory() {
@Override
public Thread newThread(@NotNull Runnable r) {
return new Thread(r ,"");
}
};
Thread thread1 = new Thread("");
ThreadFactory threadFactory3 = r -> new Thread("");
// Thread(Runnable target, String name) {
Thread t222 = new Thread(() -> {logger1.debug("before waiting"); },"");
showState(queue, threadPool);
threadPool.submit(new MyTask("1", 3600000));
showState(queue, threadPool);
threadPool.submit(new MyTask("2", 3600000));
showState(queue, threadPool);
threadPool.submit(new MyTask("3"));
showState(queue, threadPool);
threadPool.submit(new MyTask("4"));
showState(queue, threadPool);
threadPool.submit(new MyTask("5", 3600000));
showState(queue, threadPool);
threadPool.submit(new MyTask("6"));
showState(queue, threadPool);
}
private static void showState(ArrayBlockingQueue<Runnable> queue, ThreadPoolExecutor threadPool) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
List<Object> tasks = new ArrayList<>();
for (Runnable runnable : queue) {
try {
Field callable = FutureTask.class.getDeclaredField("callable");
callable.setAccessible(true);
Object adapter = callable.get(runnable);
Class<?> clazz = Class.forName("java.util.concurrent.Executors$RunnableAdapter");
Field task = clazz.getDeclaredField("task");
task.setAccessible(true);
Object o = task.get(adapter);
tasks.add(o);
} catch (Exception e) {
e.printStackTrace();
}
}
main.debug("pool size: {}, queue: {}", threadPool.getPoolSize(), tasks);
}
static class MyTask implements Runnable {
private final String name;
private final long duration;
public MyTask(String name) {
this(name, 0);
}
public MyTask(String name, long duration) {
this.name = name;
this.duration = duration;
}
@Override
public void run() {
try {
LoggerUtils.get("myThread").debug("running..." + this);
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "MyTask(" + name + ")";
}
}
}