JMM
CPU多核硬件架构
可见性
两个线程对静态成员变量的使用
没有volatile,则没办法感知到、读取到已被更改的主内存共享变量,因为线程首先会从工作内存(缓存)中读取!
共享变量修改原子操作过程
volatile可见性实现原理
通过汇编时加lock前缀指令触发底层缓存锁定机制(优先触发缓存一致性/总线锁),使得读、修改、写操作变成原子的;
总线锁(性能很低)
cpu从主存读取数据到缓存区中,总线会加锁(锁在总线上、不是cpu),其他请求会被阻塞,其他cpu核无法通过总线读取内存条,直到锁定释放
MESI缓存一致性协议
触发MESI协议,lock指令会触发锁定变量缓存行区域并写回主内存,这个操作称为“缓存锁定”
缓存一致性机制会阻止同时修改被两个以上处理器缓存的内存区域数据(MESI协议),一个处理器的缓存回写到内存内存会导致其他处理器的缓存无效(MESI协议) IA-32架构
修改的时候:S->M,锁定,发送总线写消息,其他线程丢弃工作内存内的该变量
若其他线程一开始已有该变量的再需要读,则发送总线读消息;其他线程一开始没有该变量可以直接读,性能更高
不保证非原子操作安全
可见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线程
用户线程&内核线程
创建线程池
核心线程corePoolSize为正式工,其余线程为临时工,60s没有干活就会kill掉,主线程池大小3,阻塞队列大小5,阻塞队列满了之后会触发拒绝策略
线程池5种状态
shutDown不再接收新任务,已有任务会执行完,shutDownNow安全点完全中断退出
原理
创建多线程
1、继承Thread类
重写Run方法
start方法运行
2、实现Runnable接口
推荐接口实现,继承父类只能单继承,且变量共享更方便不需static
3、实现Callable接口
4、创建线程池
核心线程corePoolSize为正式工,其余线程为临时工,60s没有干活就会kill掉,主线程池大小3,阻塞队列大小5,阻塞队列满了之后会触发拒绝策略
Thread方法
线程的生命周期
线程通信
在第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(); } } }
同步监视器注意点
同步方法的同步修饰符,非静态的同步监视器为this,静态方法的同步监视器为当前类.class
继承thread不能使用synchronized(this)作为同步监视器锁,可以使用xx.class;不可以直接使用同步方法修饰符,因为也为this!!!可以用但必须静态同步方法,此时同步监视器变为当前类.class
runnable可以使用this,也可以直接使用同步方法修饰符
同步锁ReenTrantLock
lock = new ReenTrantLock() try lock.lock ...... finally lock.unlock