线程池
线程的生命周期
线程池的组成部分
- 任务队列
- 拒绝策略(抛出异常,直接丢弃,阻塞,临时队列)
- 初始大小 init(最少线程个数) min
- 活跃大小 active
- 最大线程个数 max
关系:min<=active<=max
自定义线程池
设计模式引出的多线程问题
- 饿汉式单例设计
public class SingletonObject1 {
/**
* can't lazy load
*/
private static final SingletonObject1 instance = new SingletonObject1();
private SingletonObject1() {
}
public static SingletonObject1 getInstance(){
return instance;
}
}
- 懒加载单例设计模式
public class SingletonObject2 {
private static SingletonObject2 instance;
private SingletonObject2() {
}
public static SingletonObject2 getInstance() {
if (instance == null) {
instance = new SingletonObject2();
}
return instance;
}
}
- 同步的懒加载单例设计模式
public class SingletonObject3 {
private static SingletonObject3 instance;
private SingletonObject3() {
}
public synchronized static SingletonObject3 getInstance() {
if (instance == null) {
instance = new SingletonObject3();
}
return instance;
}
}
- DCL(Double Check Lock)
- public class SingletonObject4 {
private static SingletonObject4 instance;
private SingletonObject4() {
}
public static SingletonObject4 getInstance() {
if (instance == null) {
synchronized (SingletonObject4.class){
if (instance == null){
instance = new SingletonObject4();
}
}
}
return instance;
}
}
会出现空指针异常的问题,因为虽然成功的保证了单例原则,当其余的线程返回这个对象的时候,该类中的实例域可能还没有初始化完成
- volatile - DCL
public class SingletonObject5 {
private static volatile SingletonObject5 instance;
private SingletonObject5() {
}
public static SingletonObject5 getInstance() {
if (instance == null) {
synchronized (SingletonObject5.class){
if (instance == null){
instance = new SingletonObject5();
}
}
}
return instance;
}
}
- 静态内部类-单例
public class SingletonObject6 {
private static volatile SingletonObject6 instance;
private SingletonObject6() {
}
private static class InstanceHolder {
private static final SingletonObject6 instance = new SingletonObject6();
}
public static SingletonObject6 getInstance() {
return InstanceHolder.instance;
}
}
- 枚举-单例
public class SingletonObject7 {
private SingletonObject7() {
}
private enum Singleton {
INSTANCE;
private final SingletonObject7 instance;
Singleton() {
instance = new SingletonObject7();
}
public SingletonObject7 getInstance() {
return instance;
}
}
public static SingletonObject7 getInstance() {
return Singleton.INSTANCE.getInstance();
}
public static void main(String[] args) {
Thread[] thread = new Thread[10];
for (int i = 0; i < 10; i++) {
thread[i] = new Thread(() -> {
System.out.println(SingletonObject7.getInstance());
});
thread[i].start();
}
}
}
wait set
public class WaitSet {
private static final Object LOCK = new Object();
/**
* 1、所有的对象都会有一个wait set,用来存放调用了该对象wait方法之后进入BLOCK状态的线程
* 2、线程被notify之后,不一定立即得到执行
* 3、线程从wait set中被唤醒顺序不一定是 FIFO
* @param args
*/
public static void main(String[] args) throws InterruptedException {
IntStream.rangeClosed(1, 10).forEach(i -> new Thread(String.valueOf(i)) {
@Override
public void run() {
synchronized (LOCK) {
try {
Optional.of(Thread.currentThread().getName() + " will come to wait set.").ifPresent(System.out::println);
LOCK.wait();
Optional.of(Thread.currentThread().getName() + " will leave to wait set.").ifPresent(System.out::println);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start());
Thread.sleep(3000);
IntStream.rangeClosed(1, 10).forEach(i -> {
synchronized (LOCK) {
LOCK.notify();
}
});
}
}
并发编程中三个比较重要的概念
-
原子性 Atom
保证一个操作或者多个操作要么都成功,要么都失败,中间不能由于任何的因素中断 -
可见性
-
有序性(顺序性)
重排序只要求最终一致性
JMM怎么保证原子性
对基本数据类型的变量读取和赋值是保证了原子性的(long,double在32位的操作系统时可能不能保证原子性)
a =10 ->原子性
b=a->不满足:1.read a; 2.assign b;
c++,c=c+1;->不满足:1.read c; 2.add; 3.assign to c;
JMM怎么保证可见性
使用volatile关键字保证可见性
JMM怎么保证有序性
happens-before relationship
- 代码的执行顺序,编写在前面的发生在编写在后面的(多线程不能保证->指令重排序)
- unlock必须发生在lock之后
try{
lock.lock();
} catch(Exception e){
lock.u nlock();
}
synchronized(obj){ ---1---
}
--unlock--2---
1在2之前
- volatile修饰的变量,对一个变量的写操作先行发生于对这个变量的读操作
- 传递规则,操作A先于B,B先于C,那么A肯定先于C
- 线程启动规则,start方法肯定先于线程run()
- 线程中断规则,interrupt这个动作,必须发生在捕获该动作之后
- 对象销毁规则,初始化必须发生在finalize之前
- 线程终结规则,所有操作必须发生在线程死亡之前
volatile关键字
- 一旦一个共享变量被volatile修饰,具备两层语义:
1、保证了不同线程间的可见性(线程不经过本地内存,都从主内存中读写值)
2、禁止对其进行重排序,也就是保证了有序性
3、并未保证原子性
public class VolatileTest2 {
private static int INIT_VALUE = 0;
private final static int MAX_LIMIT = 50;
public static void main(String[] args) {
new Thread(() -> {
while (INIT_VALUE < MAX_LIMIT) {
System.out.println("T1->" + (++INIT_VALUE));
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "ADDER-1").start();
new Thread(() -> {
while (INIT_VALUE < MAX_LIMIT) {
System.out.println("T2->" + (++INIT_VALUE));
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "ADDER-2").start();
}
}
Volatile关键字
1.保证重排序的不会把后面的之类放到屏障的前面,也不会把前面的放到后面
2.强制对缓存的修改操作立刻写出主内存
3.如果是写操作,它会导致其他CPU中的缓存失效
Volatile的使用场景
- 状态栏标记
- 屏障前后的一致性
- 缓存一致性协议
- 指令重排序
- happen-before规则
- 并发编程的三要素
- volatile关键字的作用
不可变对象
- 不可变对象一定是线程安全的:里面的任何属性或者引用类型的属性都不能被修改
- 可变对象可能是安全的:StringBuffer
Future 设计模式
- Future -> 代表的是未来的一个凭据
- FutureTask -> 将你的调用逻辑进行了隔离
- FutureService -> 桥接Future和FutureTask
ThreadLocal
用于保存线程私有的数据,不同线程之间看不到彼此的ThreadLocal中保存的数据,始终以当前线程(Thread.currentThread())作为key
- ThreadLocal没放值进去时get出来为null
ThreadLocal赋初值,重写ThreadLocal的initialValue()方法;
模拟ThreadLocal的工作原理
public class ThreadLocalSimulator<T> {
private final Map<Thread, T> storage = new HashMap<>();
public void set(T t) {
synchronized (this) {
Thread key = Thread.currentThread();
storage.put(key, t);
}
}
public T get() {
Thread key = Thread.currentThread();
T value = storage.get(key);
return null == value ? initialValue() : value;
}
private T initialValue() {
return null;
}
}
ThreadLocal应用-上下文设计模式
public class Context {
private String name;
protected String cardId;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
public String getCardId() {
return cardId;
}
}
public class ActionContext {
private static final ThreadLocal<Context> threadLocal = ThreadLocal.withInitial(Context::new);
private static class InnerActionContext {
private static ActionContext actionContext = new ActionContext();
}
public static ActionContext getActionContext() {
return InnerActionContext.actionContext;
}
public Context getContext() {
return threadLocal.get();
}
}
public class ExecutionTask implements Runnable {
private QueryFromDBAction queryAction = new QueryFromDBAction();
private QueryFromHttpAction httpAction = new QueryFromHttpAction();
@Override
public void run() {
queryAction.execute();
httpAction.execute();
Context context = ActionContext.getActionContext().getContext();
System.out.println("The Name is " + context.getName() + " and CardId is " + context.getCardId());
}
}
public class QueryFromDBAction {
public void execute() {
try {
Thread.sleep(1000L);
String name = "Alex" + Thread.currentThread().getName();
ActionContext.getActionContext().getContext().setName(name);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class QueryFromHttpAction {
public void execute() {
Context context = ActionContext.getActionContext().getContext();
String cardId = getCardId(context.getName());
context.setCardId(cardId);
}
private String getCardId(String name) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "23456789" + name;
}
}
public class ContextTest {
public static void main(String[] args) {
IntStream.range(0, 100).forEach(i -> {
new Thread(new ExecutionTask()).start();
});
}
}
生产者-消费者设计模式
Count-Down设计模式
自定义ClassLoader
- 类加载器的委托是优先给 父类加载器先去尝试加载
- 父加载器和子加载器其实是一种包装关系或包含关系
总结:
定义类加载:加载类的加载器
初始类加载器:委托过程中所设计的类加载器
打破双亲委托机制,重写loadClass方法,改变类加载的顺序
import java.io.*;
/**
* @author LiSheng
* @date 2020/7/1 12:35
*/
public class MyClassLoader extends ClassLoader {
private final static String DEFAULT_DIR = "F:\\app\\classloader1";
private String dir = DEFAULT_DIR;
private String classLoaderName;
public MyClassLoader() {
super();
}
public MyClassLoader(String classLoaderName) {
this.classLoaderName = classLoaderName;
}
public MyClassLoader(String classLoaderName, ClassLoader parent) {
super(parent);
this.classLoaderName = classLoaderName;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String classPath = name.replace(".", "/");
File classFile = new File(dir, classPath + ".class");
if (!classFile.exists()) {
throw new ClassNotFoundException("The class " + name + " not found.");
}
byte[] classBytes = loadClassBytes(classFile);
if (null == classBytes || classBytes.length == 0) {
throw new ClassNotFoundException("load the class " + name + "failed");
}
return this.defineClass(name, classBytes, 0, classBytes.length);
}
//破坏双亲委托机制,重写loadclass方法,改变类加载的顺序
@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
Class<?> clazz = null;
if (name.startsWith("java.")) {
try {
ClassLoader system = ClassLoader.getSystemClassLoader();
clazz = system.loadClass(name);
if (clazz != null) {
if (resolve) {
resolveClass(clazz);
return clazz;
}
}
} catch (Exception e) {
//ignore
}
}
try {
clazz = findClass(name);
} catch (Exception e) {
}
if (clazz == null && getParent() != null) {
getParent().loadClass(name);
}
return clazz;
}
private byte[] loadClassBytes(File classFile) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
FileInputStream fis = new FileInputStream(classFile)) {
byte[] buffer = new byte[1024];
int len = 0;
while ((len = fis.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
baos.flush();
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
public static String getDefaultDir() {
return DEFAULT_DIR;
}
public String getDir() {
return dir;
}
public String getClassLoaderName() {
return classLoaderName;
}
public void setDir(String dir) {
this.dir = dir;
}
public void setClassLoaderName(String classLoaderName) {
this.classLoaderName = classLoaderName;
}
}
类加载器的命名空间
- 每个类的加载器都有子的命名空间,命名空间由该加载器及其父加载器所加载的类组成
- 在同一个命名空间中,不会出现完整的名字
运行时包
- 父加载器看不到子加载器加载的类
- 不同命名空间下的类加载器之间的类互相不可访问
类的卸载以及classLoader的卸载
- 该类的所有实例已经被GC
- 加载该类的ClassLoader实例已经被GC
- 该类的java.lang.Class对象没有在任何地方被引用
- GC的时机我们是不可控的,那么同样的我们对于Class的卸载也是不可控的