JMM

CPU多核硬件架构

可见性

两个线程对静态成员变量的使用

共享变量修改原子操作过程

volatile可见性实现原理

总线锁(性能很低)

MESI缓存一致性协议

不保证非原子操作安全

Java线程

用户线程&内核线程

创建线程池

线程池5种状态

原理

创建多线程

1、继承Thread类

重写Run方法

start方法运行

2、实现Runnable接口

3、实现Callable接口

4、创建线程池

 

Thread方法

线程的生命周期

线程通信

同步监视器注意点

同步锁ReenTrantLock


JMM

可见性、原子性、有序性

JMM定义了线程和主内存之间的抽象关系

Java多线程并发底层知识

CPU多核硬件架构

三级CPU缓存、硬件内存

Java多线程并发底层知识

可见性

两个线程对静态成员变量的使用

没有volatile,则没办法感知到、读取到已被更改的主内存共享变量,因为线程首先会从工作内存(缓存)中读取!

Java多线程并发底层知识

Java多线程并发底层知识

共享变量修改原子操作过程

Java多线程并发底层知识

工作内存可能在cpu缓存、栈(线程,在内存条)、寄存器

volatile可见性实现原理

通过汇编时加lock前缀指令触发底层缓存锁定机制(优先触发缓存一致性/总线锁),使得读、修改、写操作变成原子的;

总线锁(性能很低)

cpu从主存读取数据到缓存区中,总线会加锁(锁在总线上、不是cpu),其他请求会被阻塞,其他cpu核无法通过总线读取内存条,直到锁定释放

MESI缓存一致性协议

触发MESI协议,lock指令会触发锁定变量缓存行区域并写回主内存,这个操作称为“缓存锁定”

缓存一致性机制会阻止同时修改被两个以上处理器缓存的内存区域数据(MESI协议),一个处理器的缓存回写到内存内存会导致其他处理器的缓存无效(MESI协议) IA-32架构

多个核才会出现该并行问题,因为一个核只存在一个副本

MESI的含义,关键步骤总线嗅探读消息和写消息

Java多线程并发底层知识

并行读取的时候:E->S

Java多线程并发底层知识

修改的时候:S->M,锁定,发送总线写消息,其他线程丢弃工作内存内的该变量

若其他线程一开始已有该变量的再需要读,则发送总线读消息;其他线程一开始没有该变量可以直接读,性能更高

原线程写完消息后发送总线写回消息

Java多线程并发底层知识

不保证非原子操作安全

可见i++执行了多部操作, 从变量i中读取读取i的值 -> 值+1 -> 将+1后的值写回i中,这样在多线程的时候执行情况就类似如下了

Thread1             Thread2 r1 = i;             r3 = i;                r2 = r1 + 1;        r4 = r3 + 1; i = r2;             i = r4;

这样会造成的问题就是 r1, r3读到的值都是 0, 最后两个线程都将 1 写入 i, 最后 i 等于 1, 但是却进行了两次自增操作

可知加了volatile和没加volatile都无法解决非原子操作的线程同步问题

Java线程

用户线程&内核线程

Java多线程并发底层知识

Java线程使用KLT

Java多线程并发底层知识

创建线程池

核心线程corePoolSize为正式工,其余线程为临时工,60s没有干活就会kill掉,主线程池大小3,阻塞队列大小5,阻塞队列满了之后会触发拒绝策略

Java多线程并发底层知识

Java多线程并发底层知识

线程池5种状态

Java多线程并发底层知识

shutDown不再接收新任务,已有任务会执行完,shutDownNow安全点完全中断退出

Java多线程并发底层知识

原理

位运算

使用一个Integer保存线程池状态

-1<<29,右移29位

Java多线程并发底层知识

Java多线程并发底层知识

Java多线程并发底层知识

创建多线程

1、继承Thread类

重写Run方法

Java多线程并发底层知识

start方法运行

不能start两次,只能 再new一个

Java多线程并发底层知识

2、实现Runnable接口

Java多线程并发底层知识

Java多线程并发底层知识

推荐接口实现,继承父类只能单继承,且变量共享更方便不需static

Java多线程并发底层知识

3、实现Callable接口

重写call方法

Java多线程并发底层知识

4、创建线程池

Java多线程并发底层知识

核心线程corePoolSize为正式工,其余线程为临时工,60s没有干活就会kill掉,主线程池大小3,阻塞队列大小5,阻塞队列满了之后会触发拒绝策略

Java多线程并发底层知识

Java多线程并发底层知识

不要使用Executor创建线程池,会OMM

Thread方法

Java多线程并发底层知识

Java多线程并发底层知识

线程的生命周期

Java多线程并发底层知识

线程通信

Java多线程并发底层知识

2、三个方法的调用者必须是同步代码块的同步监视器

3、其定义在Object中的方法

在第8行,当ThreadA线程执行lock.wait();这条语句时,释放获得的对象锁lock,并放弃CPU,进入等待队列。

当另一个线程执行第23行lock.notify();,会唤醒ThreadA,但是此时它并不立即释放锁,接下来它睡眠了5秒钟(sleep()是不释放锁的,事实上sleep()也可以不在同步代码块中调用),直到第28行,退出synchronized修饰的临界区时,才会把锁释放。这时,ThreadA就有机会获得另一个线程释放的锁,并从等待的地方起(第9行)起开始执行。

public class Service {      public void testMethod(Object lock) {         try {             synchronized (lock) {                 System.out.println("begin wait() ThreadName="                         + Thread.currentThread().getName());                 lock.wait();                 System.out.println("  end wait() ThreadName="                         + Thread.currentThread().getName());             }         } catch (InterruptedException e) {             e.printStackTrace();         }     }      public void synNotifyMethod(Object lock) {         try {             synchronized (lock) {                 System.out.println("begin notify() ThreadName="                         + Thread.currentThread().getName() + " time="                         + System.currentTimeMillis());                 lock.notify();                 Thread.sleep(5000);                 System.out.println("  end notify() ThreadName="                         + Thread.currentThread().getName() + " time="                         + System.currentTimeMillis());             }         } catch (InterruptedException e) {             e.printStackTrace();         }     } }

Java多线程并发底层知识

同步监视器注意点

同步方法的同步修饰符,非静态的同步监视器为this,静态方法的同步监视器为当前类.class

继承thread不能使用synchronized(this)作为同步监视器锁,可以使用xx.class;不可以直接使用同步方法修饰符,因为也为this!!!可以用但必须静态同步方法,此时同步监视器变为当前类.class

runnable可以使用this,也可以直接使用同步方法修饰符

同步锁ReenTrantLock

lock = new ReenTrantLock() try lock.lock ...... finally lock.unlock

相关文章: