把java基础撸一边,从简单的开始。
线程部分:
对synchronize初步了解之后,知道大概原理暂时先放下,其他深入的后续再来(原因是我也还不会)。本章是对java提供的另一个关键字volatile认识一下,并使用它。
volatile 单词意思:adj. [化学] 挥发性的;不稳定的;爆炸性的;反复无常的
n. 挥发物;有翅的动物
我理解为是个可变的意思,不稳定。
可见性
在多线程开发中,对共享数据的操作是会有频繁操作的,为了保证在开发中,对会频繁变动的多线程操作数据保证一致性。java提供了synchronize,还有volatile。
在了解之前,知道一个词:可见--一个线程修改了这个变量,在另一个线程中能够读到这个修改后的值。这里注意一下,只是读到,而不是读写操作。不能保证原子性
synchronize是在保护他的代码块,不被同时两个线程进入操作出现,让线程是串行进入。重而保证了这个可见性。
volatile是怎么样保证参数的可见呢?
这里直接讲原理会好点:在创建实例的时候,加了volatile修饰词的话,在汇编中会多了一个lock指令。
lock指令:
在多处理器的系统上,1:将当前处理器缓存行的内容写回都系统内存
2:这个写回内存的操作会使其他CPU里的缓存了该内存地址的数据失效
可以理解为volatile是读锁。在内存int a 的数据被线程1影响到了CPU线程2缓存的int i的数据,但注意。这里只相信读到的数据。但不影响线程2线程3这个共享内存的数据操作。这样也就可以知道,这个volatile的局限性。它不具备原子性,只有可见性。及时更新,但不限制其他数据退volition修饰的操作。
对于他的局限性。运行做如下操作
实例:
对一先修饰也volition的数据,程序只运行一方进行操作,其他线程不允许进行更改,只可以读。这也是叫轻量级锁的原因。下面展示代码
public class A3 {
private volatile int a ;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
}复制代码
public class Demo31 {
public static void main(String[] age ){
A3 a3 = new A3();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0 ; i <= 100 ; i++){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "修改值"+i);
a3.setA(i);
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0 ; i <= 100 ; i++){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "读取值"+a3.getA());
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0 ; i <= 100 ; i++){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "读取值"+a3.getA());
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0 ; i <= 100 ; i++){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "读取值"+a3.getA());
}
}
}).start();
}
}复制代码
Thread-0修改值0
Thread-1读取值0
Thread-2读取值0
Thread-3读取值0
Thread-0修改值1
Thread-1读取值1
Thread-2读取值1
Thread-3读取值1
Thread-0修改值2
Thread-1读取值2
Thread-2读取值2
Thread-3读取值2
Thread-0修改值3
Thread-1读取值3
Thread-2读取值3
Thread-3读取值3
Thread-0修改值4
Thread-1读取值4
复制代码
可以看到,一旦线程被修改之后,读取到的数据就不会更改过来。但如果同时对数据进行修改
public class Demo31 extends Thread {
public volatile int a = 0 ;
public void set(){
for (int i = 0 ; i < 100 ; i++){
a++;
System.out.println(Thread.currentThread().getName() + " a : "+a);
}
}
@Override
public void run() {
set();
}
public static void main(String[] age ){
new Demo31().start();
new Demo31().start();
new Demo31().start();
}
}复制代码
结果
Thread-0 a : 1
Thread-1 a : 1
Thread-1 a : 2
Thread-0 a : 2
Thread-1 a : 3
Thread-2 a : 1
Thread-1 a : 4
复制代码
即使被volatile修饰之后,但并不会保证原子性。对于volatile的操作。要尽量保证是一个线程修改,其他线程只是读。
推荐文章: