1.原子性
- 和数据库中事务的原子性差不多。即一个操作或者多个操作,要么全部执行并前执行的过程不会被任何因素打断,要么就都不执行。在Java中,对基本数据类型的变量的读取和赋值都是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。
2.诸如x++等操作不是原子性
import java.util.concurrent.atomic.AtomicInteger;
/*
* 测试多线程的原子性
*/
public class ThreadAtomicDemo {
public static int x =0;
public static class AtomicThread extends Thread{
@Override
public void run() {
while(true) {
x++;//x++ x-- ++x --x都不是原子性(原子性指的是操作不能分割)
System.out.println(this.getName()+"::"+x);
try {
sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Thread t1 =new AtomicThread();
t1.setName("A");
Thread t2 =new AtomicThread();
t2.setName("B");
Thread t3 =new AtomicThread();
t3.setName("C");
t1.start();
t2.start();
t3.start();
}
}
观察上述结果,我们可以发现三个线程输出的值,和我们预想的值不一样。输出应该是从1一直递加。那么出现这种情况的原因是x++符合原子性。x++在需要进行两步操作:1.x=x;2.x++。这样就会造成线程不安全。我们可以通过使用原子操作类,例如AtomicInteger,AtomicBoolean …来解决这一问题。。。。。
import java.util.concurrent.atomic.AtomicInteger;
/*
* 测试多线程的原子性
*/
public class ThreadAtomicDemo {
//public static int x =0;
public static final AtomicInteger x = new AtomicInteger(0);//原子性
public static class AtomicThread extends Thread{
@Override
public void run() {
while(true) {
//x++;//x++ x-- ++x --x都不是原子性(原子性指的是操作不能分割)
//System.out.println(this.getName()+"::"+x);
System.out.println(this.getName()+"::"+x.incrementAndGet());//自增
try {
sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Thread t1 =new AtomicThread();
t1.setName("A");
Thread t2 =new AtomicThread();
t2.setName("B");
Thread t3 =new AtomicThread();
t3.setName("C");
t1.start();
t2.start();
t3.start();
}
}
这里原子性的实现原理,不是加锁,而是CAS(Compare And Swap)算法的实现。。。。
CAS我们后面再仔细分析。。。
2.可见性
现代计算机中,由于CPU直接从主内存中读取数据的效率不高,所以都会对应的CPU高速缓存,先将主内存中的数据读取到缓存中,线程修改数据之后首先更新到缓存,之后才会更新到主内存。如果此时还没有将数据更新到主内存其他的线程此时来读取就是修改之前的数据。
/*
* 测试多线程的可见性
*/
public class VisableThreadDemo {
public static boolean f =false;//这里必须加可见性,否则f变成true,但是A中看不到
public static class A extends Thread{
@Override
public void run() {
while(true) {
if(f) {
System.out.println(getName()+"运行:"+f);
break;
}
}
}
}
public static class B extends Thread{
@Override
public void run() {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
f =true;
System.out.println(getName()+"运行:"+f);
}
}
public static void main(String[] args) {
Thread t1 = new A();
t1.setName("A");
Thread t2 = new B();
t2.setName("B");
t1.start();
t2.start();
}
}
通过上面的程序,我们发现线程A没有被运行,因为B线程在缓存区将f改为true。主内存中的f没有改变,所以A线程没有运行。
/*
* 测试多线程的可见性
*/
public class VisableThreadDemo {
public static volatile boolean f =false;//这里必须加可见性,否则f变成true,但是A中看不到
public static class A extends Thread{
@Override
public void run() {
while(true) {
if(f) {
System.out.println(getName()+"运行:"+f);
break;
}
}
}
}
public static class B extends Thread{
@Override
public void run() {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
f =true;
System.out.println(getName()+"运行:"+f);
}
}
public static void main(String[] args) {
Thread t1 = new A();
t1.setName("A");
Thread t2 = new B();
t2.setName("B");
t1.start();
t2.start();
}
}
3.有序性
有序性,即程序执行的顺序按照代码的先后顺序执行。在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
/**
* 单例模型,关于线程的有序性测试
* @author Administrator
*
*/
public class OrderThreadDemo {
private static volatile OrderThreadDemo instance = null;
private OrderThreadDemo() {}
public static OrderThreadDemo getInstance() {
/**
* 两个线程进入方法
* if instance == null
* 准备创建对象 instance = new OrderThreadDemo();
* 可能分为三个流程:
* 1. 开辟内存空间
* 2. 初始化对象
* 3. 引用和对象绑定
*
* 另外一种方式:
* 1. 开辟内存空间
* 2. 引用和对象绑定 instance--->对象,instance不是null
* 3. 初始化对象(未完成)
*/
if(instance==null) {//判断OrderThreadDemo对象是否存在
instance = new OrderThreadDemo();
}
return instance;//多线程中可能会抛异常
}
}