java并发之原子性与可见性和多线程调用静态变量线程安全问题(转载,找不到出处)

原子性
原子是世界上的最小单位,具有不可分割性。比如 a=0;(a非long和double类型) 这个操作是不可分割的,那么我们说这个操作时原子操作。再比如:a++; 这个操作实际是a = a + 1;是可分割的,所以他不是一个原子操作。非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作。一个操作是原子操作,那么我们称它具有原子性。java的concurrent包下提供了一些原子类,我们可以通过阅读API来了解这些原子类的用法。比如:AtomicInteger、AtomicLong、AtomicReference等。
可见性
可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果。另一个线程马上就能看到。比如:用volatile修饰的变量,就会具有可见性。volatile修饰的变量不允许线程内部缓存和重排序,即直接修改内存。所以对其他线程是可见的。但是这里需要注意一个问题,volatile只能让被他修饰内容具有可见性,但不能保证它具有原子性。比如 volatile int a = 0;之后有一个操作 a++;这个变量a具有可见性,但是a++ 依然是一个非原子操作,也就这这个操作同样存在线程安全问题。
关系
原子性是说一个操作是否可分割。可见性是说操作结果其他线程是否可见。

/** 
 * 可见性分析 
 * @author Administrator 
 * 
 *volatile 会拒绝编译器对其修饰的变量进行优化。也就不会存在重排序的问题。volatile只会影响可见性,不会影响原子性。 
 *下面程序如果不加 
 */  
public class Test {  
  
    volatile int a = 1;  
    volatile boolean ready;  
      
    public class PrintA extends Thread{  
        @Override  
        public void run() {  
            while(!ready){  
                Thread.yield();  
            }  
            System.out.println(a);  
        }  
    }  
    public static void main(String[] args) throws InterruptedException {  
        Test t = new Test();  
        t.new PrintA().start();  
        //下面两行如果不加volatile的话,执行的先后顺序是不可预测的。并且下面两行都是原子操作,但是这两行作为一个整体的话就不是一个原子操作。  
        t.a = 48; //这是一个原子操作,但是其结果不一定具有可见性。加上volatile后就具备了可见性。  
        t.ready = true;//同理  
    }  
  
}  

静态变量:线程非安全
1、静态变量定义:
使用static关键字定义的变量。static可以修饰变量和方法,也有static静态代码块。
被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,
被类的所有实例共享。只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区
内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。
用public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它的类的对象时,不生成static变量的副本,而是类的所有实例共享同一个static变量。
2、静态变量使用的场景:
(1)对象间共享值时
(2)方便访问变量时
3、静态方法使用注意事项:
(1)不能在静态方法内使用非静态变量,即不能直接访问所属类的实例变量;
(2)不能在静态方法内直接调用非静态方法;
(3)静态方法中不能使用this和super关键字;
4、验证静态变量的线程安全性:
(1)静态变量是存在资源冲突问题的。
5、结论:静态变量也称为类变量,属于类对象所有,位于方法区,为所有对象共享,共享一份内存,一旦值被修改,则其他对象均对修改可见,故线程非安全。
多个线程调用同一个静态变量的解决冲突办法:
创建原子类型的静态变量,使用自带的方法自增,代码如下:

class Counter {
    private AtomicInteger count = new AtomicInteger();

    public void increment() {
        count.incrementAndGet();
    }

    // 使用AtomicInteger之后,不需要加锁,也可以实现线程安全。
    public int getCount() {
        return count.get();
    }
}

AtomicInteger常用接口:
//获取当前的值
public final int get()

//取当前的值,并设置新的值
public final int getAndSet(int newValue)

//获取当前的值,并自增
public final int getAndIncrement()

//获取当前的值,并自减
public final int getAndDecrement()

//获取当前的值,并加上预期的值
public final int getAndAdd(int delta)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值