tomcat默认150并发连接(socket)
RT:相应时间
QPS:吞吐量
cpu,内存,磁盘,网络
最大化使用硬件资源
线程数量、JVM内存大小、网络通信机制(BIO、NIO、AIO)、磁盘IO
马路车道
线程是cpu执行的最小调度单元
并行线程数量由cpu决定(cpu核心数或者核心数*2)
单核cpu也支持多线程 --> cpu时间片切换
并发:同时发生的请求连接
并行:同时处理执行的请求
需要阻塞等待其他线程完成处理
不需要阻塞当前的处理
多线程同时执行处理
public class ThreadDemo extends Thread { @Override public void run(){ //线程执行的指令 } public static void main(String[] args){ ThreadDemo t = new ThreadDemo(); t.start();//启动线程 } }
public class CallableDemo implements Callable<String> { @Override public String call(){ //call()等价于run(),线程执行的指令 return null; } public static void main(String[] args){ ExecutorService exe = Executors.newFixedThreadPool(1); CallableDemo c = new CallableDemo(); Future<String> f = exe.submit(c); f.get();//get()方法是阻塞的 } }
线程的启动 --> 结束
阻塞
public class Demo { public static void main(String[] args){ //阻塞状态 TIMED_WAITING new Thread(() -> { while(true){ try { TimeUnit.SECONDS.sleep(100); } catch (InterruptedException e){ e.printStackTrace(); } } }).start(); //阻塞状态 WAITING new Thread(() -> { while(true) { synchronized (Demo.class) { try { Demo.class.wait(); } catch (InterruptedException e){ e.printStackTrace(); } } } }).start(); //抢占锁 TIMED_WAITING new Thread(new BlockedDemo()).start(); //未抢占锁 BLOCKED new Thread(new BlockedDemo()).start(); } static class BlockedDemo extends Thread { @Override public void run() { while(true) { synchronized (Demo.class) { try { TimeUnit.SECONDS.sleep(100); } catch (InterruptedException e){ e.printStackTrace(); } } } } } }
jps 查看当前线程的Pid
jstack pid 查看pid的堆栈信息
new (线程初始化)
runnable(cpu OS调度前为就绪状态)
waiting
sleep(0);
wait();
join();
LockSupport.parkUnitl();
notify
notifyAll
unpark
timed_waiting
sleep(long);
wait(long);
join(long);
LockSupport.parkUnitl(xx);
notify
notifyAll
unpark
blocked (锁阻塞状态)
终止 (线程运行结束)
没有new状态
new Thread().start();//启动线程 new Thread().run();//调用实例方法
Thread.start方法调用本地方法start0,启动线程
JVM通过本地方法start0调用不同系统层面的创建线程
OS调用JVM的Thread.run方法(OS通过CPU调度算法调用CPU执行run方法指令)
JVM调用Java中Thread.run
执行结束之后JVM销毁线程
run方法执行结束
Thread.stop方法强制终止 ,不建议使用
发送终止信号通知终止,interrupt
Thread.currentThread().isInterrupted() 表示中断标记,默认是false
Thread.interrupt(); 设置interrupt = true
Thread.interrupted(); 复位,恢复interrupt = false
interrupt(); --> 通过一个共享变量实现线程之间的通信
设置一个共享变量的值 true 。volatile jint _interrupted;
唤醒处于阻塞状态的线程
public static int count = 0; public static incr() { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } count ++; } public static void main(String[] args) { for(int i = 0; i < 1000; i ++) { new Thread(APP::incr).start(); } try { Thread.sleep(1000); } catch(InterruptedException e) { e.printStackTrace(); } }
结果是小于等于1000的随机数
cpu时间片切换导致数据安全问题
问题出现 count ++,不是原子操作,编译成三个指令:
- 加载到寄存器
- 累加
- 写入内存
public class SynchronizedDemo { synchronized void demo1(){}//方法 synchronized static void demo2(){}//方法 Object o = new Object(); void demo3() { synchronized(o) { //代码块 } } } //锁的范围
锁的范围控制在对象实例内
SynchronizedDemo demo = new SynchronizedDemo(); new Thread(() -> { demo.demo1(); }).start(); new Thread(() -> { demo.demo1(); }).start(); void demo() { synchronized(this) { //代码块 } }
静态方法,类对象,类锁
synchronized static void demo(){} void demo() { synchronized(SynchronizedDemo.class) { //代码块 } }
共享资源
通过抢占共享资源实现排他性
对象标记 markOop _mark
类元信息 markOop _mark
实例数据 _metadata
[对齐填充]
hashcode
分代年龄
同步锁标记
偏向锁标记
偏向锁持有者线程ID
monitor() -> ObjectMonitor 争抢锁的实现逻辑
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZKaaTadW-1639183807603)(/Users/muprince/Library/Application Support/typora-user-images/image-20211124001751204.png)]
jol 查看类对象头信息布局的工具
-XX:-UseCompressedOops 关闭JVM对象头压缩指针
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.10</version> </dependency>
System.out.println(ClassLayout.parseInstance(instance).toPrintable());
偏向锁,一次CAS替换对象头线程ID,默认关闭
轻量级锁,多次CAS替换mark word中的指向栈桢的指针,自旋锁,1.6以前默认自选10次,1.6以后自适应自旋锁
重量级锁,线程阻塞
乐观锁
CAS 比较预期数据和原始数据是否一致,如果一致则修改,不一致则修改失败
monitor,重量级锁实现
生产者 – 消费者
//消费者 public class Consumer implements Runnable { private Queue<String> msg; private int maxSize; public Consumer(Queue<String> msg, int maxSize) { this.msg = msg; this.maxSize = maxSize; } @Override public void run() { while(true) { synchronized(msg) { while(msg.isEmpty()) {//队列为空,无法消费,阻塞 try { msg.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("消费者消费消息:" + msg.ramove()); msg.notify();//唤醒生产者生产 } } } } //生产者 public class Producer implements Runnable { private Queue<String> msg; private int maxSize; public Producer(Queue<String> msg, int maxSize) { this.msg = msg; this.maxSize = maxSize; } @Override public void run() { int i = 0; while(true) { i ++; synchronized(msg) { while(msg.size() == maxSize) {//队列满了,不生产,阻塞 try { msg.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("生产者生产消息:消息" + i); msg.add("消息" + i); msg.notify();//唤醒消费者消费 } } } } public class App { public static void main() { Queue<String> msg = new LinkedList<>(); int maxSize = 10; new Thread(new Producer(msg, maxSize)).start(); new Thread(new Consumer(msg, maxSize)).start(); } }
一组相互竞争共享资源的线程之间相互等待,导致永久性阻塞
活锁 线程没有出现阻塞并一直循环的状态
通过扩大锁的粒度破坏占有且等待
通过ReentrantLock.tryLock()破坏不可抢占
通过顺序加锁破坏循环等待
线程隔离机制 ,线程独有的
每个线程都持有一个ThreadLocalMap
0x61c88647 魔数,黄金分割,斐波那契散列
线性探索 -> 用来解决hash冲突的一种策略
private static final int HASH_INCREMENT = 0x61c88647; public static void main() { magicHash(16); } public static void magicHash(int size) { int hashCode = 0; for (int i = 0; i < size; i ++) { hashCode = i * HASH_INCREMENT + HASH_INCREMENT; System.out.print((hashCode & (size - 1)) + " "); } }
public class VolatileDemo { public static boolean stop = false; //public static volatile boolean stop = false; //public static volatile int i; public static void main() { new Thread(() -> { int i = 0; while(!stop) { i ++; //System.out.println("rs:" + i); 可以结束循环 //try { // Thread.sleep(0); 可以结束循环 //} catch (InterruptedException e) { // e.printStackTrace(); //} } System.out.println("rs:" + i); }).start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } stop = true; } }
无法停止线程,不能输出 rs + i
volatile 关键字修饰可以解决可见性问题
-Djava.compiler=NONE 关闭即时编译器
server版本的jdk做了深度优化(JIT) --just in time
while(!stop){ i ++; } //优化后 if(!stop) { while(true) { i ++; } }
活性失败
print中包含synchronized修饰
锁的释放会强制把工作内存的写操作同步到主内存
print是IO操作
IO操作会阻塞线程
官方说明 编译器可以自由的一次或者多次加载this.done
sleep会导致线程切换,致使缓存失效,重新加载
hsdis 打印汇编指令的工具
lock指令出发cpu核心加载缓存操作
线程A修改变量之后对线程B不可见
cpu、内存、IO设备
速度 cpu > 内存 > IO
因为高速缓存的存在,导致缓存一致性问题
cpu架构是否支持
当前数据是否存在于缓存行
MSI、MESI、MOSI…
MESI表示四种缓存状态
引入store buffer解决cpu通知缓存失效的阻塞
引入store buffer导致指令重排序
不允许指令重排序,并写入主内存
volatile 关键字通过内存屏障禁止指令重排序
防止指令重排序
禁止cpu高速缓存
不同cpu架构,X86是强一致性架构
内存模型定义了共享内存中多线程读写的操作规范
提供可见性和有序性问题的解决方案
javap -v Xx.class 查看字节指令
ACC_VOLATILE
bytecodeInterpreter.cpp
orderAccess.hpp
不能改变程序的执行结果(在单线程环境下,执行的结果不变)
依赖问题,如果两个指令存在依赖关系,不允许重排序
void test() { int a = 1; int b = 1; int c = a * b; }
a happens-before b ; b happens-before c
a happens-before b ; b happens-before c --> a happens-before c
volatile变量的写操作一定happens-before后续对于volatile变量的读操作
通过内存屏障防止指令重排序
public class VolatileExample{ int a = 0; volatile boolean flag = false; //Thread1 public void writer() { a = 1; // 1 flag = true; //修改 2 } //Thread2 public void reader() { if (flag) { //flag = true 3 int i = a; //i = 1 4 } } }
1 happens-before 2 是否成立?是
3 happens-before 4 是否成立?是
2 happens-before 3 --> volatile规则
1 happens-before 4 i = 1 成立
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I1Az0a4D-1639183807604)(/Users/muprince/Library/Application Support/typora-user-images/image-20211124052551123.png)]
int x = 10; synchronized(this) { //后续线程读取到的x的值一定是12 if (x < 12) { x = 12; } }
锁的释放一定happens-before后续对于线程加锁的操作
public class StartDemo { int x = 0; Thread t = new Thread(() -> { //读取x的值一定是20 if (x == 20){} //一定是成立 }); x = 20; t.start(); }
线程start之前的操作一定happens-before线程run操作
public class JionDemo { public static void main() { int x = 0; Thread t = new Thread(() -> { x = 20; }); t.start(); t.jion();//保证结果的可见性 //在jion之后读取到的x的值一定是20 } }
t线程中对共享变量的修改对于所有jion之后的操作可见
wait/notify,Thread实例对象锁
fatally关键字提供了内存屏障的规则