本文总结的笔记,是自己看视频来一点点总结的,供自己以后回顾,也希望能帮到大家,
Volatile关键字,内存可见性
1. 内存可见性是指当某个线程正在使用对象状态而另一个线程在同时修改状态,需要确保一个线程修改了对象状态后,其他线程能够看到发生的状态变化,简单讲就是两个线程操作一个属性,两个线程对此属性进行读写时,必须保持两个线程访问此数据的状态是同步的最新的,
2. Java提供了一种削弱的同步机制,即Volatile关键字,用来保证将变量的更新操作通知到其他线程,可以将他看成一个轻量级的锁,但是又与锁有些不同,
与锁(synchronized)的区别:
1. 对于多线程,不是一种互斥关系:synchronized锁是当一个线程访问加锁的区域的时候,别的线程是不能访问的,这就是一种互斥性,也成为互斥锁,而Volatile正好相反。
2. 不能保证变量的“原子性”,
下面是视频中一段代码,自己也实现了,但是与视频有出入
package JUC; /** * Volatile 内存可见性 */ public class VolatileP { public static void main(String[] args) { TestRun run = new TestRun(); run.run(); while (true){ if(run.isFlag()){ System.out.println("TestRun class.flag = true"); break; } } } } class TestRun implements Runnable{ private boolean flag = false; @Override public void run() { flag = true; System.out.println("flag = " + flag); } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } }
上面一段代码的意思就是,当下面的类更改flag的状态的时候,上面是否可以检测的到,并输出“TestRun class.flag = true”,视频中是不可以的,但是我本人的测试的时候是可以的,可能是因为java改进了吧,或者电脑原因也不是很清楚,,,大神知道的请及时更正我啊!
视频中是在下面的类中更改了状态后,上面是一直循环的并且没有检测到flag是更改为true状态了,仅仅输出了flag = ” + true 而已、
原子性问题的产生:
package JUC; public class AtomicTest { public static void main(String[] args) { AtomicP p = new AtomicP(); for (int i = 0; i < 10; i++) { new Thread(p).start(); } } } // 2 6 3 4 1 1 0 8 7 5 这时候就会出现问题,出现了两个 1 , /** * 所谓原子性就是要不都做要不都不做,也就是说 i++的三步操作不能因为线程的干扰而执行一半后其他任务执行完毕后在继续执行。 * 那为什么会出现这个问题呢? * int i = 10; * i = i++; //10 * 首先了解 i++的原子性问题;是分三步进行的 “读改写” * 首先i++会创建一个临时的变量, * int temp = i; * i = i+1; * i = temp; * 所以为10; * * 如上十条线程同时访问i++;如果A线程把值取过来了,正在执行 i=i+1的时候,其他线程获得了执行权,那么这时候就是两条线程同时对i的同一个值做操作,那么就会出现重复数! * * 原子变量: * jdk 1.5 之后java.util.concurrent.atomic包下提供了常用的原子变量 * 1. 其中的原子变量都是用volatile修饰的:private volatile int value; 保证了内存可见性 * 2. CAS(Compare - And - Swap)算法保证数据的原子性! * CAS算法是硬件对于并发操作共享数据的支持 * CAS包含了三个操作数 * 内存值 V * 预估值 A * 更新值 B * 当且仅当V的值等于A时,才把B的值赋值给V,否则将什么都不做 * 那这个是个啥意思呢? * 比如:现在的需要改的i值为0;那么A线程首先会先读取一下i的值,这时候A线程的V就为0,当需要更改的时候再次读取需要更改的i,发现 * 现在的值也是0,即A==0,那么B运算完事是1,现在就是V==A==0,这时候才会吧B的值赋值给i。 * 也就是再多个线程同时操作的时候,CAS算法会保证只有一个线程会成功的修改值,这时候就保证了原子性。 */ class AtomicP implements Runnable{ private int i = 0; @Override public void run() { try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i++); } }
那么怎么更改呢?这时候就会用到我们提到了Atomic包下的提供的类了。
更改如下:
package JUC; import java.util.concurrent.atomic.AtomicInteger; public class AtomicTest { public static void main(String[] args) { AtomicP p = new AtomicP(); for (int i = 0; i < 10; i++) { new Thread(p).start(); } } } class AtomicP implements Runnable{ private AtomicInteger i = new AtomicInteger(); @Override public void run() { try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } //其实这个方法就是 i++; 得到并递增 System.out.println(i.getAndIncrement()); } }
这时候就不会有问题了,那么对于CAS下面是一个模拟的程序