synchronized voliate区别,volitate与synchronized区别

  synchronized voliate区别,volitate与synchronized区别

  如何解决写爬虫IP受阻的问题?立即使用。

  可见性(visibility)

  可见性:一旦一个线程修改了一个共享变量,其他线程可以立即看到(察觉到)该变量的修改(变化)。

  Java内存模型通过将工作内存中变量的修改值同步到主存,并在读取变量之前将主存的最新值刷新到工作内存来实现可见性。

  原子性(atomicity)

  原子性:一个操作不能被中断,要么全部执行,要么不执行。

  java内存模型保证了同一个线程中的所有操作都是自顶向下的,但是当多个线程并行时,它们的操作顺序就无法保证了。

  有序性

  有序:在这个线程中观察,所有操作都是有序的;如果在一个线程中观察另一个线程,所有操作都是乱序的。

  java内存模型保证了同一个线程中的所有操作都是自顶向下的,但是当多个线程并行时,它们的操作顺序就无法保证了。

  当计算机执行一个程序时,为了提高性能,编译处理器经常会对指令进行重新排列,一般分为以下三种类型

  在单线程环境下,确保程序的最终执行结果与代码一致。

  重新排序时,处理器必须考虑指令之间的数据相关性。

  在多线程环境中,线程交替执行。由于编译器优化和重排的存在,两个线程中使用的变量能否保证所用变量的一致性是不确定的,结果也无法预测。

  先把考试中会做的事情做好,以后不会做的事情做好。

  public void mySort(){

  int x=11//1

  int y=12//2

  x=x ^ 5;//3

  y=x * x//4可能顺序1234 2134 1324,不可能属性4因为数据依赖,排在1和3之前。

  Volatile禁止指令重排。

  公共类ReSortSeqDemo {

  int a=0;

  布尔标志=假;

  公共void方法01() {

  a=1;//flag=true;

  //-线程切换-

  flag=true//a=1;

  }

  public void method02() {

  如果(标志){

  a=a 3;

  system . out . println( a= a);

  }

  }

  }如果两个线程同时执行,method01和method02如果线程1执行method01重新排序,那么切换后的线程2执行method02,就会出现不同的结果。

  禁止指令排序

  Volatile实现了禁止指令重排序的优化,从而避免了多线程环境下程序的乱序。

  我们先来理解一个概念。内存屏障又称内存屏障,是一条CPU指令,它有两个作用:

  确保具体操作的执行顺序。

  保证一些变量的内存可见性(利用这个特性实现volatile的内存可见性)

  因为编译器中的每一个处理器都可以进行指令重排序优化,如果在指令之间插入一个内存屏障,就会告诉编译器和CPU,无论什么指令,都不能对这个内存屏障指令进行重排序,也就是说,禁止通过插入内存屏障来进行内存屏障前后的重排序优化。内存屏障的另一个作用是强制刷出各种CPU缓存数据,因此CPU上的任何线程都可以读取这些数据的最新版本。

  下面是保守策略下,易失性写插入内存屏障后生成的指令序列示意图:

  下面是保守策略下,易失性读插入内存屏障后生成的指令序列示意图:

  线程安全性保证

  工作内存和主内存之间的同步延迟会导致可见性问题。

  它可以通过使用synchronzied或volatile关键字来解决,通过使用一个线程的修改变量,其他线程可以立即看到这些关键字。

  对于指令重排,它会导致可见性和排序问题。

  可以用volatile关键字解决,因为volatile的另一个功能是禁止指令重排序优化。

  volatile

  修改后的变量不保留副本,而是直接访问主存中的。

  在Java内存模型中,有主内存,每个线程也有自己的内存(如寄存器)。为了提高性能,线程会在自己的内存中保存一份要访问的变量的副本。这样,在某个时刻,同一个变量在一个线程内存中的值可能与另一个线程内存中的值不一致,或者与主存中的值不一致。变量被声明为volatile,这意味着它可以在任何时候被其他线程修改,因此它不能缓存在线程内存中。

  使用场景

  您只能在有限的情况下使用volatile变量来代替锁。为了让volatile变量提供理想的线程安全,必须同时满足以下两个条件:

  1)对变量的写操作不依赖于当前值。

  2)该变量不包含在其他变量的不变量中。

  Volatile最适合一个线程写,多个线程读的情况。

  如果有多个线程并发写入,您仍然需要使用锁或线程安全容器或原子变量。

  synchronized

  当用来修改一个方法或者一个代码块时,可以保证同一时间最多只有一个线程可以执行这段代码。

  当两个并发线程访问同一个object对象中的这个synchronized(this)同步代码块时,一次只能执行一个线程。另一个线程必须等待当前线程执行完该代码块后才能执行它。然而,当一个线程访问一个对象的同步代码块时,另一个线程仍然可以访问该对象中的非同步代码块。特别是,当一个线程访问对象的同步(this)同步代码块时,其他线程将被阻止访问对象中所有其他同步(this)同步代码块。当线程访问对象的同步(this)同步代码块时,它获取对象的对象锁。结果,其他线程对object对象的所有同步代码部分的访问被暂时阻塞。

  共享资源和对象的添加、删除和修改。锁

  从jdk 5.0开始,java提供了更强大的线程同步机制——通过显示和定义同步锁对象来实现同步,并将同步锁作为锁对象。

  Java。util.concurrent.locks.lock接口是控制多个线程访问共享资源的工具。Lock提供对共享资源的独占访问。一次只有一个线程可以锁定锁对象。线程应该在开始访问共享资源之前获得锁对象。

  ReentrantLock类实现了Lock,它具有与synchronized相同的并发性和内存语义。在线程安全的控制上,常用的是ReentrantLock,可以显示锁定和释放锁。

  区别

  volatile和synchronized

  Volatile是一个变量修饰符,而synchronized作用于一段代码或方法。

  Volatile只在线程内存和“主”内存之间同步变量的值;Synchronized通过锁定和解锁监视器来同步所有变量的值。显然,synchronized比volatile消耗更多的资源。

  Volatile不会造成线程阻塞;同步可能会导致线程阻塞。

  Volatile保证了数据的可见性,但是不能保证原子性;Synchronized可以间接保证原子性和可见性,因为它会同步私有内存和公共内存中的数据。

  编译器不会优化由volatile标记的变量;同步标记变量可以由编译器优化。

  线程安全包括原子性和可见性,Java的同步机制就是围绕这两个方面来保证线程安全的。

  volatile这个关键字主要用在可以感知到实例变量在多线程中被修改,并且可以获得最新值的情况下,也就是多线程在读取共享变量时可以获得最新值。

  关键字volatile提示线程每次从共享内存而不是私有内存读取变量,从而保证同步数据的可见性。但是,应该注意,如果修改实例变量中的数据

  比如:I,也就是I=I ^ 1,那么这个操作实际上不是原子操作,也就是说它不是线程安全的。I表达式操作步骤分解如下:

  1)从内存中取出I的值。

  2)计算I的值;

  3)将I的值写入内存。

  如果在步骤2中计算I的值时,另一个线程也修改了该值,name此时将具有脏读数据。解决方案是使用synchronized关键字。所以volatile本身并不处理数据的原子性,而是强制数据的读写及时影响主存。

  synchronized 和Lock

  Lock是显示锁(手动开关,别忘了关锁),synchronized是隐式锁,超出范围自动释放。只有锁代码块锁,而synchronized可以作用于代码块和方法。

  有了Lock lock,jvm在调度线程上花费的时间更少,性能更好。并且具有更好的扩展性(提供更多子类)。

  使用顺序:Lock- synchronize代码块(已经进入方法体并分配了相应的资源)- synchronize方法(在方法体之外)。

  更多编程知识请访问:编程学习课程!这就是volatile和synchronize的区别。有什么区别?更多详情请关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: