标签(空格分隔): 进程/线程 操做系统java
Java虚拟机提供的轻量级的同步机制c++
不一样的线程进入共享内存中读取数据以后, 在各自的工做空间对数据一通操做, 而后写入共享内存中, 这个时候由于共享内存的数据改变, 这个时候会通知其余读取该共享变量的线程, 通知该数据已经改变.函数
/** * 1. 验证Volatile的可见性. * 1.1 加入number=0; number没有添加Volatile关键字修饰---没有可见性. * 1.2 在该线程对其私有虚拟机栈中栈帧中的备份number操做以后, 不会将数据覆盖到主内存当中. * 2. 加入Volatile以后 * 2.1 在线程对其备份修改完毕以后, 会将数据覆盖到主内存当中. */ public class VolatileDemo { public static void main(String[] args) { val myData = new MyData(); new Thread(() -> { System.out.println(Thread.currentThread().getName() + "\t come in"); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } myData.addTo60(); System.out.println(Thread.currentThread().getName() + "\t updated number value: " + myData.number); }, "aaa").start(); while (myData.number == 0) { } System.out.println("任务结束, number: " + myData.number); } } class MyData { // volatile int number = 0; int number = 0; public void addTo60() { this.number = 60; } public void addPlusPlus() { this.number++; } }
/** * Volatile 不保证原子性 * * 在多个线程对栈帧中的number修改完毕以后, 在A线程立刻开始写入主内存的时候被打断了, 这个时候B线程把本身的计算结果写入了 * 这个时候, 就会产生计算结果被覆盖的状况. 而后永远都计算不出来正确的值. 这个就是Volatile的原子性问题. */ public class VolatileDemo1 { public static void main(String[] args) throws InterruptedException { MyData myData = new MyData(); for (int i = 0; i < 20; i++) { new Thread(() -> { for (int j = 0; j < 1000; j++) { myData.addPlusPlus(); } }, String.valueOf(i)).start(); } /* main线程 和 后台GC线程 */ while(Thread.activeCount()>2){ Thread.yield(); } System.out.println(myData.number); System.out.println("剩余线程数量"+ Thread.activeCount()); } } class MyData { /* 经过 java.util.concurrent.atomic 包去 实现原子性操做. 自旋锁 * 能够看到AtomicInteger构造函数发现 最后存储的方式 private volatile int value; * */ AtomicInteger atomicInteger = new AtomicInteger(1); public int addAtomic(){ return atomicInteger.getAndIncrement(); } }
指令队列在CPU执行时不是串行的, 当某条指令执行时消耗较多时间时, CPU资源足够时并不会在此无心义的等待, 而是开启下一个指令. 开启下一条指令是有条件的, 即上一条指令和下一条指令不存在相关性. 例以下面这个例子:this
a /= 2; // 指令A a /= 2; // 指令B c++; // 指令C
这里的指令B是依赖于指令A的执行结果的, 在A处于执行阶段时, B会被阻塞, 直到A执行完成. 而指令C与A/B均没有依赖关系, 因此在A执行或者B执行的过程当中, C会同时被执行, 那么C有可能在A+B的执行过程当中就执行完毕了, 这样指令队列的实际执行顺序就是 C->A->B 或者 A->C->B.atom
可能出现指令重排致使问题的代码操作系统
public void method1() { a = 1; // 语句1 flag = true; // 语句2 } public void method2() { if (flag) { a = a + 5; } }
工做区域和主内存出现的同步延迟现象致使的可见性问题可使用synchronize或volatile解决. 他们均可以使一个线程修改后的变量当即对其余线程可见.线程
运算由运算器单元(ALU)实现,指令包括算术运算指令、逻辑运算指令和移位指令。
算术运算指令实现加减乘除(+-*/)等基本的算术运算;逻辑运算指令实现与或非(&|~)等基本的逻辑运算;移位指令实现二进制比特位(bit)的左右移(<<>>)运算。code
除了作计算外,CPU还要实现循环。循环是由跳转指令实现的,跳回去执行就是循环。循环在必定条件下跳出,不然就成死循环了,条件跳转指令能完成这个功能。条件跳转指令在必定条件下实现跳转,它能实现分支功能。跳转指令也称为控制指令。控制由CPU控制器单元实现。队列
运算和控制指令的操做数从哪里来的呢?操做数都放在存储器中。在x86 IA中,运算指令的操做数既能够是寄存器,也能够是存储器;而在其余RISCIA例如MIPS中,运算指令的操做数只能是寄存器,所以须要先使用加载(load)指令将存储器中的数据导入到寄存器中,运算完成后,再用存储(store)指令将寄存器中的运算结果数据导出到存储器中。这类指令就是数据传送指令。进程
可见性的实现就是 数据传送指令中的先使用加载(load)指令将存储器中的数据导入到寄存器中,运算完成后,再用存储(store)指令将寄存器中的运算结果数据导出到存储器中。