1.原子性:
操作是不可分割的,就说这个操作是原子操作。比如a++; 这个操作实际是a = a + 1;是可分割的,所以他不是一个原子操作。非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作。无论是直接的还是间接的,几乎 java.util.concurrent 包中的所有类都使用原子变量,而不使用同步。
java的concurrent包下提供了一些原子类,比如:AtomicInteger、AtomicLong、AtomicReference等。如果一段代码被认为是Atomic,则表示这段代码在执行过程中,是不能被中断的。
java.util.concurrent这个包里面提供了一组原子类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。实际上是借助硬件的相关指令来实现的,不会阻塞线程(或者说只是在硬件级别上阻塞了)。
2.可见性:
是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果,另一个线程马上就能看到。比如:用volatile修饰的变量,就会具有可见性。volatile修饰的变量volatile 会拒绝编译器对其修饰的变量进行优化,不允许线程内部缓存和重排序,即直接修改内存。所以对其他线程是可见的,但是这里需要注意一个问题,volatile只能让被他修饰内容具有可见性,不能保证它具有原子性。比如 volatile int a = 0;之后有一个操作 a++;这个变量a具有可见性,但是a++
依然是一个非原子操作,也就这这个操作同样存在线程安全问题。
在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。