对象及变量的并发访问

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dream_follower/article/details/83994961

1 synchronized同步方法

1.1 方法内的变量为线程安全

非线程安全问题存在于实例变量中,如果是方法内部的私有变量,则不存在非线程安全问题,我觉得这个是因为每一次调用方法,都会在内存中生成一个新的局部变量。

1.2 实例变量非线程安全

如果多个线程共同访问一个对象中的实例变量,则有可能出现“非线程安全”的问题,用线程访问的对象中如果有多个实例变量,则运行的结果有可能出现交叉的情况,如果对象仅有一个实例变量,则有可能出现覆盖的情况

1.3synchronized

synchronized取得的锁是对象锁,而不是把一段代码或者方法(函数)当作锁,如果多个线程访问多个对象,则JVM会创建多个锁,如果A线程先持有对象C的锁,那么B线程就无法获取对象C的锁,B线程需要等待,这个过程称为同步,但是B线程可以通过异步的方式调用对象C中的非synchronized方法,并且synchronized还拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁的。当一个线程执行的代码出现异常时,其所持有的锁会自动释放。

1.4脏读

所谓脏读就是指一个线程读取实例变量时,但是这个值已经被其他线程改过了。解决办法就是加上synchronized关键字.
在这里插入图片描述
同步不具有继承性,也就是说需要在子类中添加synchronized关键字

2synchronized(this)同步语句块

用关键字synchronize声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个长时间的任务,那么B线程则必须等待比较长时间,在这种情况下可以使用synchronized关键同步语句块来解决。当两个并发线程访问同一个对象object的synchronized(this)同步代码块时,一段时间只能有一个线程被执行,另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。并且在使用synchronized(this)代码块时,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个oject中所有其他synchronized(this)同步代码块的访问将被阻塞。这说明synchronized使用的“对象监视器”是一个。synchronized(this)代码块是锁定当前对象的,

2.1任意对象作为对象监视器

在这里插入图片描述
在这里插入图片描述
使用synchronized(非this对象)同步代码块时,对象监视器必须是同一个对象,如果不是同一个对象监视器时,运行的结果就是异步调用了。就相当于使用了不同的锁
同步代码块放在非同步方法中进行声明,并不能保证调用方法的线程的执行同步/顺序性,也就是线程调用方法的顺序是无序的。多个线程调用同一个方法是随机的,

2.2关于synchronized(非this对象)的三个结论

在这里插入图片描述
第1点有一个前提就是它们使用的对象必须得是同一个对象。
第2点,就是比如说两个线程A和B,A和B通过一个service访问同一个对象C,C对象里有一个自定义的同步方法,service里面也有同步代码块,在一个方法里,同步的对象是C,然后如果A先调用service里面的那个包含同步代码块的方法,B调用C里面的自定义的同步方法,这个就会出现同步的效果,B只有等A执行完了,才可以接着执行。
第3点,和第2点实际上是一样的,只不过,对象C里面定义的不是同步方法,而是synchronized(this)同步对象。

2.3静态同步synchronized方法与synchronized(class)代码块

关键字synchronized还可以应用在static静态方法上,如果那样写就是对当前的*.java文件对应的class类进行持锁。
synchronized关键字加到static静态方法上是给class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁。class锁可以对类的所有实例起作用,而对象锁只对一一个对象起作用,也就是,比如说有两个线程A和B,分别要访问两个不同的对象C和D,但是由于加了class锁,即使同时启动这两个线程,这两个线程也不是异步的,是同步的,B线程必须得等到A线程执行完才可以接着执行。

2.4数据类型String的常量池特性

在JVM中具有String常量池缓存的功能,也就是说如果使用字符串常量作为锁的话,如果不同的进程使用的字符串是同一个字符串的话,那么他们使用的就是同一个锁。

3volatile 关键字

volatile的主要作用是使变量在多个线程之间可见

3.1关键字volatile与死循环

volatile关键字的作用就是强制从公共栈堆中取得变量的值,而不是从线程私有数据栈中取得变量的值。
在这里插入图片描述
在这里插入图片描述
同步性就是原子性。
关键字volatile主要使用的场合是在多个线程中可以感知实例变量被更改了,并且可以获得最新的值使用,也就是用多线程读取共享变量时可以获得最新值使用。关键字volatile提示线程每次从共享内存中读取变量,而不是从私有内存中读取,这样就保证了同步数据的可见性,但是有一点需要注意的是:如果修改实例变量中的数据,比如i++,则这样的操作并不是原子操作,也就是非线程安全的,这个表达式可以分为3步:

  1. 从内存中取出i的值
  2. 计算i的值
  3. 将i的值写入到内存中
    假如在第2步计算值的时候,另外一个线程也修改i的值,那么这个时候就会出现脏数据

3.2使用原子类进行i++操作

除了在i++操作时使用synchronized关键字实现同步外,还可以使用AtomicInteger原子类进行实现。原子操作是不能分割的主体,没有其他线程能够中断或检查正在原子操作中的变量,一个原子类型就是一个原子操作可用的类型,它可以在没有锁的情况下做到线程安全。原子类也并不完全安全,原子类在具有逻辑性的情况下输出结果也具有随机性。这个问题的原因是方法和方法之间的调用却不是原子的。

猜你喜欢

转载自blog.csdn.net/dream_follower/article/details/83994961