怎么搭建自己公司网站,制作网站公司 可以要求后续修改吗,哈尔滨今天重大新闻,wordpress主页页面模板一、java内存模型提到同步、锁#xff0c;就必须提到Java的内存模型#xff0c;为了提高程序的执行效率#xff0c;java也吸收了传统应用程序的多级缓存体系。在共享内存的多处理器体系架构中#xff0c;每个处理器都拥有自己的缓存#xff0c;并且定期地与主内存进行协调…一、java内存模型提到同步、锁就必须提到Java的内存模型为了提高程序的执行效率java也吸收了传统应用程序的多级缓存体系。在共享内存的多处理器体系架构中每个处理器都拥有自己的缓存并且定期地与主内存进行协调。在不同的处理器架构中提供了不同级别的缓存一致性(Cache Coherence),其中一部分只提供最小的保证即允许不同的处理器在任意时刻从同一个存储位置上看到不同的值。操作系统、编译器以及运行时(有时甚至包括应用程序)需要弥合这种在硬件能力与线程安全之间的差异。要想确保每个处理器都能在任意时刻知道其他处理器正在进行的工作将需要非常大的开销。在大多数时间里这种信息是不必要的。因此处理器会适当放宽存储一致性保证以换取性能的提升。在架构定义的内存模型中将告诉应用程序可以从内存系统中获得怎样的保证此外还定义了一些特殊的指令(称为内存栅栏)当需要共享数据时这些指令就能实现额外的存储协调保证。为了使java开发人员无须关心不同架构内存模型之间的差异Java还提供了自己的内存模型并且JVM通过在适当的位置上插入内存栅栏来屏蔽在JVM与底层之平台内存模型之间的差异。经过上面的讲解和上图我们知道线程在运行时候有一块内存专用区域Java程序会将变量同步到线程所在的内存。这时候会操作工作内存中的变量而线程中的变量何时同步回到内存是不可预期的。但是java内存模型规定通过关键词”synchronized“、”volatile“可以让java保证某些约束。“volatile” - 保证读写的都是主内存变量。“synchronized” - 保证在块开始时都同步主内存值到工作内存而快结束时将工作内存同步会主内存。重排序public classPossibleReordering {static int x 0,y0;static int a0,b0;public static void main(String[] args) throwsInterruptedException {Thread one new Thread(newRunnable() {Overridepublic voidrun() {a 1;xb;}});Thread two new Thread(newRunnable() {Overridepublic voidrun() {b 2;ya;}});one.start();two.start();one.join();two.join();System.out.println(x: x,y:y);}}重排序。如上图执行结果一般人可能认为是11真正的执行结果可能每次都不一样。拜JMM重排序所赐JMM使得不同线程的操作顺序是不同的从而导致在缺乏同步的情况下要推断操作的执行结果将变得更加复杂。各种使操作延迟或看似乱序执行的不同原因都可以归为重排序。内存级的重排序会使程序的行为变得不可预测。如果没有同步要推断出程序的执行顺序是非常困难的而要确保在程序中正确的使用同步却是非常容易的。同步将限制编译器和硬件运行时对内存操作重排序的方式。锁synchronized锁实现了对临界资源的互斥访问被synchronized修饰的代码只有一条线程可以通过是严格的排它锁、互斥锁。没有获得对应锁对象监视器(monitor)的线程会进入等待队列任何线程必须获得monitor的所有权才可以进入同步块退出同步快或者遇到异常都要释放所有权JVM规范通过两个内存屏障(memory barrier)命令来实现排它逻辑。内存屏障可以理解成顺序执行的一组CPU指令完全无视指令重排序。什么是锁public class TestStatic {public syncronized static void write(boolean flag) {xxxxx}public synchronized static void read() {xxxxx}}线程1访问TestStatic.write()方法时线程2能访问TestStatic.read()方法吗线程1访问new TestStatic().write()方法时线程2能访问new TestStatic().read()方法吗线程1访问TestStatic.write()方法时线程2能访问new TestStatic().read()方法吗public class Test {public syncronized void write(boolean flag) {xxxxx}public synchronized void read() {xxxxx}}Test test new Test();线程1访问test.write() 方法线程2能否访问test.read()方法Test a new Test(); Test b new Test();线程1访问a.write()访问线程2能否访问b.read()方法答案java中每个对象都可以作为一个锁而对象就决定了锁的粒度大小。对于实例同步方法锁是当前对象。对于静态方法锁是TestSTatic.class对象对于同步代码块锁是Synchronized括号里面配置的对象TestStatic类1问作用范围全体class对象线程1拿到线程2就不能拿到2问3问同上Test类1问不能锁都是实例对象test线程1拿到锁之后线程2无法访问2问可以线程1锁是实例a线程2是实例b。独占锁如果你不敢确定该用什么锁就用这个吧在保证正确的前提下后续在提高开发效率。public class ServerStatus {public final Set users;public final Set quers;public synchronized void addUser(String u ) {users.add(u);}public synchronized void addQuery(String q ) {quers.add(q);}public synchronized void removeUser(String u) {users.remove(u);}public synchronized void removeQuery(String q) {quers.remove(q);}}分拆锁如果在整个应用程序只有一个锁而不是为每个对象分配一个独立的锁那么所有同步代码块的执行就会变成串行化执行。由于很多线程都会竞争同一个全局锁因此两个线程同时请求这个锁的概率将会剧增从而导致更严重的竞争。所以如果将这些锁请求分到更多的锁上就能有效降低锁竞争程度。由于等待而被阻塞的线程将更少从而可伸缩性将提高。上文中users、quers是两个相互独立的变量可以将此分解为两个独立的锁每个锁只保护一个变量降低每个锁被请求的频率。public class ServerStatus {public final Set users;public final Set quers;public void addUser(String u ) {synchronized(users) {users.add(u);}}public void addQuery(String q ) {synchronized(quers) {quers.add(q);}}public void removeUser(String u) {synchronized(users) {users.remove(u);}}public void removeQuery(String q) {synchronized(quers) {quers.remove(q);}}}分离锁在某些情况下可以将锁分解技术进一步扩展为对一组独立对象上的锁进行分解这种情况称为锁分段。例如ConcurrencyHashMap是有一个包含16个锁的数组实现每个锁保护所有散列桶的1/16其中第N个散列桶由第(N mod 16)个锁来保护。假设所有关键字都时间均与分布那么相当于把锁的请求减少到原来的1/16可以支持多达16个的并发写入。锁分段的劣势在于与采用单个锁来实现独占访问相比要获取多个锁来实现独占访问将更加困难并且开销更高比如计算size、重hash。分布式锁zookeeper判断临时节点是否存在存在就说明已经有人争抢到锁不存在就创建节点表明拥有该锁。记下以后详细研究《分布式锁实现数据库、redis、zookeeper》volatilevolatile是比synchronized更轻量级的同步原语volatile可以修饰实例变量、静态变量、以及数组变量(网上大牛说维护的是引用但是里面的对象。。。嘿嘿嘿)。被volatile修饰的变量JVM规范规定一个线程在修改完另外的线程能读取最新的值。但仅仅保证可见性不保证原子性所以volatile通常用来修饰boolean类型或者状态比较少的数据类型而且不能用来更新依赖变量之前值的操作(例volatile)。volatile内部仅仅是对变量的操作多了一条cpu指令(lock#指令)它会强制写数据到缓存如果缓存数据同时也在主存会强制写数据更新到主存并且使所有持有该主存数据地址的缓存统统失效触发其他持有缓存数据的线程从主存获取最新数据从而实现同步。