当前位置: 首页 > news >正文

网站外链多的危害python怎么开发网站

网站外链多的危害,python怎么开发网站,深圳网站设计公司费用是,建设 市民中心网站1、java内存模型 Java内存模型#xff08;Java Memory Model#xff0c;JMM#xff09;是Java编程语言中用于处理并发编程的一组规则和规范。它定义了Java程序中多线程之间如何交互以及内存如何被共享和访问的规则。它定义了主内存#xff0c;工作内存的抽象概念#xff0…1、java内存模型 Java内存模型Java Memory ModelJMM是Java编程语言中用于处理并发编程的一组规则和规范。它定义了Java程序中多线程之间如何交互以及内存如何被共享和访问的规则。它定义了主内存工作内存的抽象概念底层对应着 CPU 寄存器、缓存、硬件内存、 CPU 指令优化等。 主内存Main Memory主内存是所有线程共享的内存区域包含了所有的变量、实例对象和类信息。工作内存Working Memory每个线程都有自己的工作内存用于存储主内存中的部分数据副本。线程对变量的读写操作都在工作内存中进行。 JMM体现在以下几个方面 原子性Atomicity指的是对于基本数据类型例如int、long等的读写操作是原子的即不可分割的。这意味着其他线程要么看到完整的操作结果要么看不到。可见性Visibility可见性指的是一个线程对变量的修改能否被其他线程立即看到。在Java内存模型中如果一个线程修改了共享变量的值其他线程可能不会立即看到这个变化除非采取特定的同步机制。有序性Ordering指的是程序执行的顺序必须遵循一定的规则。在没有同步机制的情况下指令的执行顺序可能会被重排序但是Java内存模型保证适当使用同步机制时指令的顺序不会被打乱。 Q1主内存和工作内存有什么区别和联系 主内存和工作内存都是Java内存模型中的概念用于描述线程之间的内存交互和数据共享。 主内存中的数据是线程之间共享的所有线程都可以读取和修改主内存中的数据。当一个线程对主内存中的数据进行修改时其他线程可能不会立即看到这个变化除非采取特定的同步机制来确保可见性。 此外主内存是所有线程共享的内存区域而工作内存是每个线程私有的内存区域。主内存存储了所有的变量和实例对象而工作内存中只包含了线程需要使用的部分数据副本。线程对变量的读写操作都在工作内存中进行不同线程之间的操作不会直接影响到主内存需要通过特定的机制来同步和确保可见性。 2、可见性 2.1、案例一 首先我们来看一个案例 Slf4j(topic c.Demo1) public class Demo1 {static boolean flag true;public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(() - {while (flag){}},t1);t1.start();Thread.sleep(1000);log.info(t1线程停止);flag false;} } 此案例期望的结果是主线程在睡眠1s后将标记设置为false然后t1线程的条件不满足就停止运行。 最终结果虽然主线程在睡眠1s后将标记修改成了false但是t1线程没有停止运行。 2.2、问题分析 为什么会造成上面的结果答案是与JMM中的可见性有关。 初始状态下主内存中会记录成员变量run的值为true此时t1线程读取的是主内存中的值。 因为 t 线程要频繁从主内存中读取 run 的值JIT 编译器会将 run 的值缓存至自己工作内存中的高速缓存中 减少对主存中 run 的访问提高效率。如下图类似于redis的缓存。 主线程睡眠了1s之后修改run的值为false此时修改的是主存中的run。但是t线程还是在从工作内存中读取run造成读取的结果永远是旧的值。类似于redis缓存不一致问题。 2.3、解决方案 Slf4j(topic c.Demo1) public class Demo1 {//从主内存获取最新值volatile static boolean flag true;public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(() - {while (flag){}},t1);t1.start();Thread.sleep(1000);log.info(t1线程停止);flag false;} }在成员变量上使用volatile关键字。 它可以用来修饰成员变量和静态成员变量他可以避免线程从自己的工作缓存中查找变量的值必须到主存中获取 它的值线程操作 volatile 变量都是直接操作主存。 2.4、可见性原子性 可以确保变量的可见性但不能保证原子性。原子性是指对变量的读取和写入操作是不可分割的要么全部完成要么全部不完成。volatile关键字只能保证对被volatile修饰变量的读取和写入操作在内存中是可见的但不能保证复合操作的原子性。 例如我有一个成员变量volatile int i 0;虽然加了volatile关键字保证多线程间对于i修改是即时可见的但是如果我要进行i操作因为i操作在字节码层面不是原子性的只是加上volatile关键字无法解决线程时间片到期上下文切换的问题。 Q1synchronized关键字能够保证可见性吗 当一个线程获取到对象的锁或类的锁并执行进入临界区代码时它会清空工作内存中的数据从主内存中重新读取变量的值确保获取到最新的值。当线程释放锁时会将对变量的修改刷新回主内存从而保证了可见性。近似于redis缓存策略的先删除缓存再更新数据库。 具体来说,synchronized保证了以下几点 获取锁时刷新变量值线程获取锁时会清空工作内存中的数据并重新从主内存中获取变量的最新值确保线程获取到的是最新的值。释放锁时刷新变量值线程在释放锁之前会将对变量的修改刷新回主内存使得其他线程在获取锁后能够看到最新的值。同一时刻只有一个线程能够执行对变量的修改操作不会被多个线程同时执行避免了竞态条件和可见性问题。 但是有一个前提条件只有被synchronized完全控制的变量才可以保证可见性。 2.5、使用volatile改造两阶段终止模式 Slf4j(topic c.TwoStagesBreak) public class TwoStagesBreak {public static void main(String[] args) throws InterruptedException {//使用volatiles改造两阶段中止模式log.info(Thread.currentThread().getName() -run);Monitor monitor new Monitor();monitor.startMonitor();//当前线程睡眠3s后执行打断操作Thread.sleep(3000);log.info(stop monitor);monitor.stopMonitor();} }Slf4j(topic c.Monitor) class Monitor {private Thread monitor;private volatile Boolean flag false;private Boolean isStart false;/*** 开始监控线程*/public void startMonitor() {synchronized (this) {if (isStart) {return;}isStart true;}monitor new Thread(() - {log.info(Thread.currentThread().getName() -run);while (true) {if (flag) {log.info(Monitor interrupted);break;}try {Thread.sleep(2000);//睡眠时非正常打断log.info(执行监控);} catch (InterruptedException e) {}}});monitor.start();}/*** 打断线程监控*/public void stopMonitor() {flag true;monitor.interrupt();} } 打断线程监控方法中此时将被volatile关键字修饰的flag改为true因为flag被volatile修饰所以修改对监视线程同步可见。 再次调用监视线程的interrupt方法是为了如果在监视线程睡眠时打断使其进入catch块不再多执行一次执行监控相关代码。 如果多个线程同时访问startMonitor方法可能会开启多次监控。此时我们采用了Balking模式解决这个问题在一个线程发现另一个线程或本线程已经做了某一件相同的事那么本线程就无需再做 了直接结束返回。 在上面的案例中引入了isStart变量。并且对监控线程使用了同步代码块。当线程一进入方法争抢到了锁会判断isStart因为是第一次调用所以isStart null然后线程一会将isStart设置成true执行剩下的代码并且释放锁当线程二争抢到锁后发现此时isStart ! null就直接返回避免重复执行。 3、有序性 有序性指的是程序执行的顺序必须遵循一定的规则。在多线程环境中由于指令执行的并发性可能会导致指令的执行顺序发生变化从而产生意料之外的结果。为了解决这个问题Java内存模型定义了一系列规则确保适当使用同步机制时指令的顺序不会被打乱保证程序的有序性。 3.1、指令重排    而有序性又引出了指令重排的概念 指令重排是指处理器或编译器为了优化程序执行速度而对指令执行顺序进行重新排序的过程。在现代计算机系统中为了提高性能处理器和编译器可能会对指令进行重排但这种重排不能影响程序的最终结果必须保证程序的语义不变。 因为i和j都是赋值操作并且互相不依赖先执行给i赋值和先执行给j赋值没有必然的先后顺序的联系。所以可以是 也可以是 但是在多线程下的指令重排也可能会造成以下的问题 数据竞争Data Race如果指令重排导致多个线程对共享数据的操作顺序发生变化可能会导致数据竞争问题从而产生不确定的结果。可见性问题指令重排可能导致某些变量的修改对其他线程不可见破坏了程序的可见性。 3.2、案例二 常规情况下可能会有的结果 1、先执行线程二线程二全部执行完成后执行线程一此时的值为4。 2、先执行线程一线程一全部执行完在执行线程二此时的值为1。 3、线程二执行完成赋值操作此时切换到线程一值为1。 但是因为上文所说指令重排序的问题最终的执行结果还有可能为0。 我们可以使用volatile关键字解决这样的问题。 4、volatile原理 volatile 的底层实现原理是内存屏障Memory BarrierMemory Fence。 对 volatile 变量的写指令后会加入写屏障。 对 volatile 变量的读指令前会加入读屏障。 写屏障sfence保证在该屏障之前的对共享变量的改动都同步到主存当中 而读屏障lfence保证在该屏障之后对共享变量的读取加载的是主存中最新数据。 写屏障会确保指令重排序时不会将写屏障之前的代码排在写屏障之后读屏障会确保指令重排序时不会将读屏障之后的代码排在读屏障之前从而保证了有序性。 例如在上面的代码中ready变量使用了volatile关键字上一行的num2就不会排在readytrue的后面。 但是并不能保证读屏障不会排在写屏障的前面只能保证写屏障前的所有代码不会排在写屏障的后面。有序性也只能保证本线程中代码的有序不能控制其他线程。 5、双检锁单例模式问题 5.1、存在的问题分析 Slf4j(topic c.Demo3) public final class Singleton {private Singleton() {}private static Singleton INSTANCE null;public static Singleton getInstance() {if (INSTANCE null) { // t2// 首次访问会同步而之后的使用没有 synchronizedsynchronized (Singleton.class) {if (INSTANCE null) { // t1INSTANCE new Singleton();}}}return INSTANCE;} } 上面是一个单例模式的实现。其具有以下特点 懒惰实例化并非随着类的加载而加载。首次使用 getInstance() 才使用 synchronized 加锁后续使用时无需加锁。第一个 if 使用了 INSTANCE 变量是在同步块之外这就会导致多线程并发运行时会存在问题。 下面从字节码的层面分析一下问题 字节码的含义 getstatic #2获取静态字段 ifnonnull 37如果静态字段不为 null则跳转到第 37 行意味着单例对象已经被创建无需再次创建。 ldc #3 将常量池中索引为 3 的类引用推送至栈顶 dup复制栈顶数值并将复制值压入栈顶此时栈顶有两个相同的类引用。 astore_0将栈顶引用类型数值存储到局部变量表中第 0 个位置。 monitorenter进入对象的监视器锁定用于实现线程同步下面是对单例对象的创建部分需要加锁。 getstatic #2再次获取静态字段检查是否已经被其他线程创建。 ifnonnull 27如果静态字段不为 null则跳转到第 27 行意味着单例对象已经被其他线程创建无需再次创建。 new #3 创建一个新实例并将其引用推送至栈顶。 dup复制栈顶数值并将复制值压入栈顶此时栈顶有两个相同的单例对象引用。 invokespecial #4 调用对象构造函数 init 进行初始化。 putstatic #2 将栈顶的单例对象引用存储到静态字段中完成单例对象的创建。 aload_0将局部变量表中第 0 个位置的引用类型数值加载到栈顶。 monitorexit退出对象的监视器释放锁解锁完成对象创建。 goto 37跳转到第 37 行即整个单例创建流程结束。 astore_1将栈顶引用类型数值存储到局部变量表中第 1 个位置用于异常处理。 aload_0将局部变量表中第 0 个位置的引用类型数值加载到栈顶即单例对象引用。 monitorexit在异常处理时再次退出对象的监视器释放锁确保锁的正常释放。 aload_1将局部变量表中第 1 个位置的引用类型数值加载到栈顶即异常对象引用。 athrow将栈顶的异常对象引用抛出。 getstatic #2最后再次获取静态字段确保单例对象已经被创建。 areturn将栈顶的单例对象引用返回给调用者。 我们重点看标红的四行。在执行过程中JIT即时编译器有可能会对指令进行重排序先将栈顶的单例对象引用存储到静态字段中完成单例对象的创建。再调用对象构造函数 init 进行初始化。 上述的流程在单线程环境下是没有问题的但是在多线程环境下可能存在t1线程获取到了锁执行了INSTANCE new Singleton();但是由于字节码中是先创建了对象导致INSTANCE不为null此时t2线程执行到了INSTANCE null的if判断此行不在锁范围内不满足判断条件直接返回了INSTANCE。此时返回的INSTANCE还没有调用构造方法一些可能存在的初始化的代码还没有执行也就是对象没有创建完全。 5.2、解决方案 我们可以通过volatile关键字解决上述问题 Slf4j(topic c.Demo3) public final class Singleton {private Singleton() {}private static volatile Singleton INSTANCE null;public static Singleton getInstance() {if (INSTANCE null) { // t2// 首次访问会同步而之后的使用没有 synchronizedsynchronized (Singleton.class) {if (INSTANCE null) { // t1INSTANCE new Singleton();}}}return INSTANCE;} } 为什么加上volatile关键字就能解决问题此前我们有提到过voltile是通过读写屏障来保证可见性和有序性。 如上图所示因为成员变量INSTANCE被volatile修饰所以INSTANCE new Singleton();这一行给INSTANCE赋值操作具有写屏障会保证字节码操作不会出现指令重排序一定是先调用了构造方法再给INSTANCE赋值。而INSTANCE null具有读屏障会保证读取到的都是主存中最新的值。且不会将读屏障之后的代码排在读屏障之前。 此时即使发生极端情况线程t1在执行INSTANCE new Singleton();时线程t2进行了INSTANCE null的if判断由于t1未执行完成赋值操作t2从主存中读取到的依旧是null。 6、happens-before 6.1、基本概念 Happens-before 是 Java 内存模型Java Memory ModelJMM中的一个概念用于描述多线程环境下操作之间的可见性和顺序性规则。它指定了对一个变量的写操作对于后续对该变量的读操作是可见的这是为了保证多线程程序的正确性和可预测性而设计的一种规范。 具体来说如果一个操作 A happens-before 另一个操作 B那么 A 在时间上先于 B并且在执行时会对 B 产生一定的影响确保 B 能够看到 A 对共享变量所做的修改。 6.2、案例 线程解锁 m 之前对变量的写对于接下来对 m 加锁的其它线程对该变量的读可见 static int x; static Object m new Object();new Thread(()-{synchronized(m) {x 10;} },t1).start();new Thread(()-{synchronized(m) {System.out.println(x);} },t2).start(); 线程对 volatile 变量的写对接下来其它线程对该变量的读可见 volatile static int x;new Thread(()-{x 10; },t1).start();new Thread(()-{System.out.println(x); },t2).start(); 线程 start 前对变量的写对该线程开始后对该变量的读可见 static int x; x 10;new Thread(()-{System.out.println(x); },t2).start(); 线程结束前对变量的写对其它线程得知它结束后的读可见比如其它线程调用 t1.isAlive() 或 t1.join()等待 它结束 static int x;Thread t1 new Thread(()-{x 10; },t1);t1.start(); t1.join(); System.out.println(x);线程 t1 打断 t2interrupt前对变量的写对于其他线程得知 t2 被打断后对变量的读可见通过 t2.interrupted 或 t2.isInterrupted static int x; public static void main(String[] args) {Thread t2 new Thread(()-{while(true) {if(Thread.currentThread().isInterrupted()) {System.out.println(x);break;}}},t2);t2.start();new Thread(()-{sleep(1);x 10;t2.interrupt();},t1).start();while(!t2.isInterrupted()) {Thread.yield();}System.out.println(x);} 对变量默认值0falsenull的写对其它线程对该变量的读可见。 以及具有传递性如果 A happens-before B且 B happens-before C那么 A happens-before C。 volatile static int x; static int y; new Thread(()-{ y 10;x 20; },t1).start();new Thread(()-{// x20 对 t2 可见, 同时 y10 也对 t2 可见System.out.println(x); },t2).start();7、线程安全单例案例 解答 使用final标记为最终可以避免子类继承父类破坏单例。在类中加上Object readResolve()方法在反序列的过程中如果该方法返回了对象就会使用该方法返回的对象而不是反序列化过程中产生的对象。设置为私有是为了避免外界调用构造方法创建对象但是不能防止反射创建新的实例setAccessible(true)可以保证线程安全静态成员变量的初始化是在类加载时完成类加载由jvm保证线程安全。封装成方法更加灵活可以对初始化成员变量的过程中进行一些控制例如实现懒加载。 解答 枚举中的每个值都相当于其静态成员变量。而上面提到静态成员变量的初始化是在类加载时完成类加载由jvm保证线程安全。同上。不能。可以被反序列化因为枚举类都默认实现了序列化接口但是在实现中考虑到了这样的问题所以不会被破坏单例。属于饿汉式枚举中的每个值都相当于其静态成员变量。而上面提到静态成员变量的初始化是在类加载时完成。可以加上构造方法进行逻辑处理。 解答线程是安全的在静态方法上加锁相当于锁住类的.class。同一时刻只能有一个线程进入该方法。缺点是锁的范围过大会影响性能。 解答 为了保证成员变量INSTANCE在初始化时的有序性和可见性避免字节码操作指令重排序返回未初始完成的INSTANCE对象。缩小锁的范围有利于提高性能。INSTANCEnull的if判断没有加锁避免两个线程同时判断为null其中一个线程获取到了锁初始化并返回了对象释放锁第二个线程拿到锁后再次初始化对象。 解答 属于懒汉式对象随着类的加载而创建没有静态成员变量的初始化是在类加载时完成类加载由jvm保证线程安全。
http://www.zqtcl.cn/news/114563/

相关文章:

  • c 网站开发类似优酷乐山旅游英文网站建设
  • 网站空间租用哪家好小程序免费制作平台企业中心
  • 个人网站可以做哪些主题网站别人做的收到方正侵权
  • 网站seo最新优化方法网络营销做的好的企业
  • 做网站如何防止被坑做的网站第二年续费多钱
  • 做网站注意哪些方面聊城住房建设局网站
  • ftp wordpress 搬站太仓做网站公司
  • php美食网站开发背景天津微外卖网站建设
  • 如何建造企业网站北京金山办公软件公司
  • dedecms织梦搬家公司网站模板贵阳国家经济技术开发区门户网站
  • 网站架构设计师网络工程师的就业前景
  • 网站建设所需人员世界各国o2o响应式网站
  • 成都网站设计最加科技企业宣传片观后感
  • 人社门户网站建设方案非官方网站建设
  • 深圳系统网站开发做家具定制的设计网站
  • 网站制作学费多少钱网络推广的常用方法
  • 个人作品网站模板百度上做网站需要钱吗
  • 苏州网站建设行业研究思路 网站建设
  • 金泉网做网站找谁网站的结构布局
  • 网站开发摊销年限柳州网站建设哪家
  • 佛山市和城乡建设局网站首页武建安装公司新闻
  • 如何宣传商务网站网页制作与设计自考
  • 在国内的服务器上建设国外网站响应式单页网站模板
  • 平湖市住房建设局网站国外代理ip
  • 铁路建设监理网站地推项目发布平台
  • 我的世界做指令的网站网站如何在推广
  • 过年做那个网站致富盘锦网站建设vhkeji
  • 网站semseo先做哪个关键词投放
  • 药品招商网站大全南阳做网站公司电话
  • 优秀手机网站大学生创新产品设计作品