目录
一、原子性
1、原子性原理
2、测试代码
二、可见性
1、可见性原理
2、测试代码
2.1、synchronized和lock也能实现可见性
2.2、添加volatile关键字也能实现可见性
三、有序性
1、有序性原理
2、测试代码
一个操作或者多个操作要么全部执行并且执行的过程不被任何因素打断,要么都不执行。
按正常的运行结果来说它应该是20000,但运行结果却并不是,产生的原因是有几个线程点,两个线程都读完了然后进行加一操作,都赋值给a因此才导致只加了一次。
public class Test03 { public static int a=0; public static void main(String[] args) throws InterruptedException { for (int i=0;i<10000;i++){ new Thread(new Runnable() { @Override public void run() { a++; } }).start(); new Thread(new Runnable() { @Override public void run() { a++; } }).start(); } Thread.sleep(1000); System.out.println(a); } }
解决办法我们可以采用synchronized和Lock来实现,由于synchronized和Lock能够保证任意时刻只有一个线程执行该代码块,那么就保证了原子性。
可见性是指当多个线程访问同一个变量的时候,一个线程修改了这个变量的值,其他线程能够立即看到修改后的值。
改代码会一直处于运行状态,因为其线程无法实现线程的同步,因为普通的共享变量是不能够保证可见性的,因为普通变量修改之后不知道何时会写入内存,因此很难实现可见性。
public class Test01 { private static boolean run=true; private static void test(){ System.out.println("start------"); while (run){ } System.out.println("end--------"); } public static void main(String[] args) throws InterruptedException { new Thread(Test01::test).start(); Thread.sleep(10000); run=false; } }
在该线程的基础上添加了一个输出
public class Test01 { private static boolean run=true; private static void test(){ System.out.println("start------"); while (run){ System.out.println("hallo"); } System.out.println("end--------"); } public static void main(String[] args) throws InterruptedException { new Thread(Test01::test).start(); Thread.sleep(10000); run=false; } }
从结果可以看出它是实现可见性的,因为println里面是有synchronized,因为synchronized和locl也是能够保证可见性的,因为他们能够保证只有一个线程能够获取锁,并且在释放锁的时候能够实现将跟新的数据写入到内存当中。
public class Test01 { private static volatile boolean run=true; private static void test(){ System.out.println("start------"); while (run){ System.out.println("hallo"); } System.out.println("end--------"); } public static void main(String[] args) throws InterruptedException { new Thread(Test01::test).start(); Thread.sleep(10000); run=false; } }
volatile能够实现对该线程只有发生变化则会立即跟新到内存中,并且让其他线程能够及时可见。
有序性是指程序执行的顺序按照代码的先后顺序执行。
该代码主要是当x=0和y=0时线程才会停止,因此从结果可以明白,在Java内存模型中,允许编译器和处理器对我们所编译的代码进行重排序但要保证其不会影响到最终的结果的正确性。因此下述代码并没有保证有序性。
public class Test02 { public static int x=0,y=0; public static int a=0,b=0; public static void main(String[] args) throws InterruptedException { for (long i=0;i<Long.MAX_VALUE;i++){ x=0;y=0;a=0;b=0; CountDownLatch latch = new CountDownLatch(2); Thread one = new Thread(new Runnable() { @Override public void run() { a=1; x=b; latch.countDown(); } }); Thread other = new Thread(new Runnable() { @Override public void run() { b=1; y=a; latch.countDown(); } }); one.start(); other.start(); latch.await(); String result="第"+i+"次(x"+x+","+y+")"; if(x==0&&y==0){ System.out.println(result); break; } } } }
想要实现有序性我们可以考虑volatile因为它有两个作用一个是保持可见一个是禁止重排,当然我们也可以通过synchronized和Lock来保证每个时刻是有一个线程执行同步代码,相当于让线程顺序执行同步代码,就可以保证其有序性。