java volatile与Synchronized知识点了解

java多线程下,如果对一个共享变量进行并发操作的话会引发线程安全问题

前提了解

volatile关键字:

      并发环境下,每个线程都会有自己的工作内存,每个线程只能访问各自的工作内存,而共享变量会被加载到每个线程的工作内存中,所以这里面就有一个问题,内存中的数据什么时候被加载到线程的工作内存中,而线程工作内存中的内容什么时候会回写到变量内存中去。这两个步骤处理不当就会造成数据的不一致,比如某个共享变量被线程A修改了,但是没有回写到内存中去,而线程B在加载了内存中的数据之后读取到的共享变量是脏数据,正确的做法应该是线程A的修改应该对线程B是可见的。

  对于内存可见性的一点补充是,之所以会造成多个线程看到的共享变量的值不一样,是因为线程在占用CPU时间的时候,cpu为了提高处理速度不会直接和内存交互,而是会先将内存中的共享内容读取到内部缓存空间中,然后cpu在处理的过程中就只会和内部缓存空间交互,在多核心的机器中这样的处理方式就会造成内存可见性问题。

  volatile可以解决并发环境下的内存可见性问题,只需要在共享变量前面加上volatile关键字就可以解决,但是需要说明的是,volatile仅仅是解决内存可见性问题,对于像i++这样的问题还是需要使用其他的方式来保证线程安全。使用volatile解决内存可见性问题的原理是,如果对被volatile修饰的共享变量执行写操作的话,JVM就会向cpu发送一条Lock前缀的指令,cpu将会这个变量所在的缓存行(缓存中可以分配的最小缓存单位)写回到内存中去。但是在多处理器的情况下,将某个cpu上的缓存行写回到系统内存之后,其他cpu上该变量的缓存还是旧的,这样再进行后面的操作的时候就会出现问题,所以为了使得所有线程看到的内容都是一致的,就需要实现缓存一致性协议,cpu将会通过监控总线上传递过来的数据来判断自己的缓存是否过期,如果过期,就需要使得缓存失效,如果cpu再来访问该缓存的时候,就会发现缓存失效了,这时候就会重新从内存加载缓存。

Synchronized关键字:

  synchronized 除了保障原子性外,也保障了可见性。因为 synchronized 无论是同步的方法还是同步的代码块,都会先把主内存的数据拷贝到工作内存中,同步代码块结束,会把工作内存中的数据更新到主内存中,这样主内存中的数据一定是最新的。更重要的是禁用了乱序重组以及保证了值对存储器的写入,这样就可以保证可见性。

Synchronized同步方法:

同步方法只影响锁定同一个锁对象的同步方法。不影响其他线程调用非同步方法,或调用其他锁资源的同步方法。

  1.synchronized关键字同步的时候,等待的线程将无法控制,只能死等。

  2.synchronized关键字同步的时候,不保证公平性,因此会有线程插队的现象。

        用关键字 synchronized 声明方法在某些情况下是有弊端的,比如 A 线程调用同步方法执行一个较长时间的任务,那么 B 线程必须等待比较长的时间。这种情况下可以尝试使用 synchronized 同步代码块来解决问题。

Synchronized同步代码块:

  同步代码块的同步粒度更加细致,是开发中推荐的编程方式。可以定位到具体的同步位置,而不是简单的将方法整体实现同步逻辑。在效率上,相对更高。

  把需要同步的代码块包起来,注意不要把耗时的操作放在同步代码块中。比如打印输出、IO 操作等等。

volatile关键字作用

volatile保证内存数据可见性,不能保证内存数据的原子性操作

synchronized关键字作用

synchronized保证内存数据可见性,也保证了内存数据的原子性操作

猜你喜欢

转载自blog.csdn.net/Growing_hacker/article/details/109098523