
这方面来说,小游戏网站,视频网站,小说网站有得天独厚的优势,用户体验比较高的web2.0社区网站也可以做到这点,资讯门户或者博客类网站就相对来说比较难了,因为很多用户到资讯类网站和博客类网站都是看完想知道的内容就会直接离开。如今越来越多的seo新手站长选择用个人博客类型的网站练手,确实,在没有合适资源的情况下,个人博客网站非常适合新手seo练手,网站优化好还能成为自己一份体面的简历。2、博客软文,虽然现在的博客外链已经被搜索引擎降低了,但是笔者仍建议各位菜鸟站长建立博客群转载软文,等到博客权重上来之后自然效果就明显了,养博需要注意的是,平常建立十个左右的博客,每天给博客转载一些qq空间或者别的地方与自己网站无关的文章,然后隔三差五的转一篇自己网站的文章,带上自己网站链接,**这样做的好处是能保证博客的可持续发展,笔者之前建立过一些博客,只转载自己网站的文章,每几天就被封了,**的新浪博客。
Java内存模型,即Java Memory Model,简称JMM,它是一种抽象的概念,或者是一种协议,用来解决在并发编程过程中内存访问的问题,同时又可以兼容不同的硬件和操作系统,JMM的原理与硬件一致性的原理类似。在硬件一致性的实现中,每个CPU会存在一个高速缓存,并且各个CPU通过与自己的高速缓存交互来向共享内存中读写数据。
如下图所示,在Java内存模型中,所有的变量都存储在主内存。每个Java线程都存在着自己的工作内存,工作内存中保存了该线程用得到的变量的副本,线程对变量的读写都在工作内存中完成,无法直接操作主内存,也无法直接访问其他线程的工作内存。当一个线程之间的变量的值的传递必须经过主内存。
当两个线程A和线程B之间要完成通信的话,要经历如下两步:
线程A从主内存中将共享变量读入线程A的工作内存后并进行操作,之后将数据重新写回到主内存中;
线程B从主存中读取最新的共享变量
volatile关键字使得每次volatile变量都能够强制刷新到主存,从而对每个线程都是可见的。

需要注意的是,JMM与Java内存区域的划分是不同的概念层次,更恰当说JMM描述的是一组规则,通过这组规则控制程序中各个变量在共享数据区域和私有数据区域的访问方式。在JMM中主内存属于共享数据区域,从某个程度上讲应该包括了堆和方法区,而工作内存数据线程私有数据区域,从某个程度上讲则应该包括程序计数器、虚拟机栈以及本地方法栈。
上面介绍了JMM中主内存和工作内存交互以及线程之间通信的原理,但是具体到各个内存之间如何进行变量的传递,JMM定义了8种操作,用来实现主内存与工作内存之间的具体交互协议:
lock unlock read load use assign store write
如果要把一个变量从主内存中复制到工作内存,就需要按顺寻地执行read和load操作,如果把变量从工作内存中同步回主内存中,就要按顺序地执行store和write操作。Java内存模型只要求上述两个操作必须按顺序执行,而没有保证必须是连续执行。也就是read和load之间,store和write之间是可以插入其他指令的,如对主内存中的变量a、b进行访问时,可能的顺序是read a,read b,load b,load a。
Java内存模型还规定了在执行上述八种基本操作时,必须满足如下规则:
不允许read和load、store和write操作之一单独出现;
不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中;

不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中;
一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作;
一个变量在同一时刻只允许一条线程对其进行lock操作,lock和unlock必须成对出现;
如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值;
下面的例子没有使用同步方法,即读写volatile变量是不执行加锁操作,不使执行的线程阻塞,因此volatile变量是一种比synchronized更轻量级的通过不机制。类型变量,执行enter时,会包装成object对象,多个线程多次锁定1的object,是多个对象,不是同一个对象,和想要锁定同一个对象不和,但是lock做了限制,只能是引用变量。它允许应用程序在执行一个可能不安全或敏感的操作前确定该操作是什么,以及是否是在允许执行该操作的安全上下文中执行它。
对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)。
此外,虚拟机还对voliate关键字和long及double做了一些特殊的规定。
保证变量的可见性:当一个被voliate关键字修饰的变量被一个线程修改的时候,其他线程可以立刻得到修改之后的结果。当一个线程向被voliate关键字修饰的变量写入数据的时候,虚拟机会强制它被值刷新到主内存中。当一个线程用到被voliate关键字修饰的值的时候,虚拟机会强制要求它从主内存中读取。
对同一个线程没有数据依赖的指令可以重排序优化执行,有数据依赖的指令按照程序顺序执行,从而保证单线程程序运行的正确性。只要某个线程中无法检测到重排序的情况(即使在其他线程中可以很明显的看到该线程中的重排序),那么就无法确保线程中的操作将按照程序中指定的顺序来执行。对于单线程下,我们不可避免程序中出现io操作,但如果我们能在自己的程序中(即用户程序级别,而非操作系统级别)控制单线程下的多个任务能在一个任务遇到io阻塞时就切换到另外一个任务去计算,这样就保证了该线程能够最大限度地处于就绪态,即随时都可以被cpu执行的状态,相当于我们在用户程序级别将自己的io操作最大限度地隐藏起来,从而可以迷惑操作系统,让其看到:该线程好像是一直在计算,io比较少,从而更多的将cpu的执行权限分配给我们的线程。
我们以下面的程序为例来说明voliate是如何防止指令重排序:
public class Singleton {
private volatile static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) { // 1
sychronized(Singleton.class) {
if (singleton == null) {
singleton = new Singleton(); // 2
}
}
}
return singleton;
}
}
复制代码
接口中不能使用静态语句块,但仍然有变量初始化的赋值操作。java内存模型要求变量的读取和写入操作必须是原子性的,但对于非volatile类型的double和long变量,jvm允许将64为读或写操作分解成两个32位操作。因此在多线程环境中对一个变量进行读写时,我们需要有一种方法能够保证对一个值的递增操作是原子操作——即不可打断性,一个线程在执行原子操作时,其它线程必须等待它完成之后才能开始执行该原子操作。
给 singleton 分配内存
调用 Singleton 的构造函数来初始化成员变量
将 singleton 对象指向分配的内存空间(执行完这步 singleton 就为非 null 了)

如果虚拟机存在指令重排序优化,则步骤2和3的顺序是无法确定的。如果A线程率先进入同步代码块并先执行了3而没有执行2,此时因为singleton已经非null。这时候线程B到了1处,判断singleton非null并将其返回使用,因为此时Singleton实际上还未初始化,自然就会出错。
对代码块中定义的变量(包含函数中定义的变量),当执行到变量声明语句时,系统为这些auto变量分配空间,当程序流程离开代码块时,这些变量被自动撤销,其占用的内存空间被释放。虽然所有的变量都需要声明,但是我们可以在使用它们之前的任何时候声明这些变量(这一点与c++一致,即变量声明需要放在一个代码块中靠前的语句里)。内存屏障的确有一些开销 —— 编译器/cpu 不能重排序指令,会潜在的导致代码没有尽可能高效的利用 cpu,而且刷新 cpu cache 会对性能有明显的影响。
操作并不具备原子性,该操作是先读取值,然后写回一个新值,相当于原来的值加上1,分两步完成,如果第二个线程在第一个线程读取旧值和写回新值期间读取i的域值,那么第二个线程就会与第一个线程一起看到同一个值,并执行相同值的加1操作,这也就造成了线程安全失败,因此对于increase方法必须使用synchronized修饰,以便保证线程安全。当你把练习本拿出来以后,把你的单词书拿出来,看着单词然后每个单词写10遍,把例句在写1遍,在写的时候你千万记住,例如employer,你就应该这样写,employer(雇主),边写边读,最重要的是还要把单词分成字母读,例如e,m,p,l,o,y,e,r,边写边读,当你写完十个单词后,你会发现,远比你拿着书看强,单词也会在你心中印象非常深刻。在mvcc并发控制中,读操作可以分成两类:快照读 (snapshot read)与当前读 (currentread)。
Java内存模型是通过各种操作定义的,JMM为程序中所有的操作定义了一个偏序关系,就是先行发生原则(Happens-before)。它是判断数据是否存在竞争、线程是否安全的主要依据。想要保证执行操作B的线程看到操作A的结果,那么在A和B之间必须满足Happens-before关系,否则JVM就可以对它们任意地排序。
先行发生原则主要包括下面几项,当两个变量之间满足以下关系中的任意一个的时候,我们就可以判断它们之间的是存在先后顺序的,串行执行的。
程序次序规则(Program Order Rule) 管理锁定规则(Monitor Lock Rule) volatile变量规则(Volatile Variable Rule) 线程启动规则(Thread Start Rule) 线程终止规则(Thread Termination Rule) 线程中断规则(Thread Interruption Rule) 对象终结规则(Finilizer Rule) 传递性(Transitivity)
不同操作时间先后顺序与先行发生原则之间没有关系,二者不能相互推断,衡量并发安全问题不能受到时间顺序的干扰,一切都要以先行发生原则为准。
如果两个操作访问同一个变量,且这两个操作有一个为写操作,此时这两个操作就存在数据依赖性这里就存在三种情况:1).读后写;2).写后写;3). 写后读,三种操作都是存在数据依赖性的,如果重排序会对最终执行结果会存在影响。编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖性关系的两个操作的执行顺序。
还有就是as-if-serial语义:不管怎么重排序(编译器和处理器为了提供并行度),(单线程)程序的执行结果不能被改变。编译器,runtime和处理器都必须遵守as-if-serial语义。as-if-serial语义保证单线程内程序的执行结果不被改变,happens-before关系保证正确同步的多线程程序的执行结果不被改变。
《合同法》第229条规定,租赁物在租赁期间发生所有权变动的,不影响租赁合同的效力,即所谓的“买卖不破租赁”原则,若无法定或约定的除外情形,该原则即应被适用。我们只能从“不受保护”即不适用该法第8条规定的信赖保护原则,而不适用信赖保护原则的情形只能是无效行政行为,推断基于欺骗、贿赂等不正当手段而实施的行政许可行为属于无效行政行为。因此,协议双方应当明确情势变更原则的适用情形或者排除情形,共同确认实现既定业绩目标的风险属于正常商业风险的范畴,以此杜绝争议。
本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-109490-1.html
索罗斯