每日问答(2)java中如何实现线程安全

目录

一、实现线程安全主要有四种方法

二、怎么样给共享变量的操作加上同步

  1、可见性:

  2、原子性:

  3、顺序性:


一、实现线程安全主要有四种方法

(1)线程间不要跨线程访问共享变量。

(2)使共享变量是final类型的。

(3)使共享变量只读。

(3)将共享变量的操作加上同步(主要讲怎么加上同步)

二、怎么样给共享变量的操作加上同步

如果想保证线程安全则必须实现共享变量的可见性、原子性和顺行性,Java中怎么保证这三个特性呢?接下来让我一一道来。

 1、可见性:

(1)概念:当多个线程并发访问共享变量时,一个线程对共享变量的修改,其它线程能够立即看到。此处涉及到计算机硬件层面的机制,线程的工作内存和主内存数据复制延迟导致的不可见性。 

(2)保证:Java中可通过volatile关键字来保证可见性,当一个变量被volatile修饰时,当值被修改时,各个线程内的缓存值会立刻被刷新,从而达到可见性。

  2、原子性:

(1)概念:这件事要么全做完,要么全不做。这就是原子性的概念,举个经典的例子----比如A和B同时向C转账10万元。如果转账操作不具有原子性,A在向C转账时,读取了C的余额为20万,然后加上转账的10万,计算出此时应该有30万,但还未来及将30万写回C的账户,此时B的转账请求过来了,B发现C的余额为20万,然后将其加10万并写回。然后A的转账操作继续——将30万写回C的余额。这种情况下C的最终余额为30万,而非预期的40万。

(2)保证:Java可通过锁和同步来保证原子性。这种方式又被称为悲观锁。

(i)锁——lock:使用锁,可以保证同一时间只有一个线程能拿到锁,也就保证了同一时间只有一个线程能执行申请锁和释放锁之间的代码。锁的打开我关闭都是需要手动设置的。例如:

public void lockTest () {
  lock.lock();
  try{
    int j = i;
    i = j + 1;
  } finally {
    lock.unlock();
  }
}

      (ii)同步代码块——synchronized:同步代码块和锁达到是一个功能,同一时间只能有一个线程执行锁住的代码,在synchronized的使用时要注意,在非静态资源时,他的拥有者是该类的实例,所以锁住的是实例;为静态资源时,则锁住的是该类的class对象。

public void lockTest () {
  synchronized (Object){
    int j = i;
    i = j + 1;
  }
}

关于synchronized和lock的区别请参考:Lock和synchronized的区别和使用

关于悲观锁和乐观锁的知识请参考:最通俗易懂的乐观锁与悲观锁原理及实现

3、顺序性:

 (1)概念:代码的顺序和最后JVM执行的顺序不一定是相同的,因为JVM为了更快速的运行,需要对执行顺序进行调整,可能有人会说了,WLGCD,顺序变了?别着急,虽然顺序变了,但是不影响最后的结果,也就是在单线程内是安全的,没有影响的,但是,这可能会影响到多线程的安全,举个例子说:(例子抄袭)

线程A:

context = loadContext();    
inited = true;   

线程B:

while(!inited ){
 sleep
}
doSomethingwithconfig(context);

如果线程B发生了重排序:

inited = true;    
context = loadContext();

首先对于线程A本身是没有影响的,因为这个重排序对于线程A来说是不会影响线程A的正确性的,而如果loadContext()方法被阻塞了,为了增加Cpu的利用率,这个重排序是可能的。

如果要防止重排序,需要使用volatile关键字,volatile关键字可以保证变量的操作是不会被重排序的。这个人讲的好——这个人

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值