线程变量的可见性:
一个线程修改了这个变量的值,在另外一个线程中能够读到这个修改后的值。
Synchronized除了线程之间互斥意外,还有一个非常大的作用,就是保证可见性,比如以下例子中get和set方法被synchronized关键字修饰,他们两个同时的可见变量是a;synchronized可以保证同一个实例,在多线程的环境下get和set方法操控的变量能保持一致。
/**
* 保证可见性的前提
* 多个线程拿到的是同一把锁,否则是保证不了的。
*
*/
public class Demo {
private int a = 1;
public synchronized int getA() {
return a;
}
public synchronized void setA(int a) {
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.a = a;
}
public static void main(String[] args) {
Demo demo = new Demo();
new Thread(new Runnable() {
@Override
public void run() {
demo.setA(10);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(demo.getA());
}
}).start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("最终的结果为:" + demo.getA());
}
}
Volatile称之为轻量级锁,被volatile修饰的变量,在线程之间是可见的。如下例子第一个线程执行完毕后第二个线程执行。一个线程修改变量后,另一个线程马上可见。
public class Demo2 {
public volatile boolean run = false;
public static void main(String[] args) {
Demo2 d = new Demo2();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 1;i<=10;i++) {
System.err.println("执行了第 " + i + " 次");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
d.run = true;
}
}).start();
new Thread(new Runnable() {
//第一个线程执行完毕后,第二个线程执行
@Override
public void run() {
while(!d.run) {
// 不执行
}
System.err.println("线程2执行了...");
}
}).start();
}
}
Volatile的字节码指令相当于Lock指令
将当前处理器缓存行的内容写回到系统内存。
写回到内存的操作会使在其他CPU里缓存了该内存地址的数据失效 ,保证多线程下变量数据的一致性。
无法解决多线程环境下,对变量的原子性操作如自增
JDK提供的原子类原理及使用
java.util.concurrent.atomic
原子更新基本类型
import java.util.concurrent.atomic.AtomicInteger;
public class Sequence {
private AtomicInteger value = new AtomicInteger(0);
public int getNext() {
return value.getAndIncrement();
}
public static void main(String[] args) {
Sequence s = new Sequence();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() + " " + s.getNext());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
原子更新数组
private int [] s = {2,1,4,6};
AtomicIntegerArray a = new AtomicIntegerArray(s);
原子更新抽象类型
//主要操作get和set方法
AtomicReference<User> user = new AtomicReference<>();
原子更新字段
//需要操作哪个字段,字段必须要经过volatile修饰
AtomicIntegerFieldUpdater<User> old = AtomicIntegerFieldUpdater.newUpdater(User.class, "old");
User user = new User();
//进行自增操作
System.out.println(old.getAndIncrement(user));