synchronized
synchronized可以修饰方法,代码块。若修饰的是非静态方法,则锁是对象锁,若修饰的是静态方法,则锁是类锁。若修饰代码块则需要填一个参数作为锁。
synchronized可以保证原子性。
volatile
以一个例子来说明volatile的其中一个作用
public class Test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
while (true) {
if(myThread.isFlag()){
System.out.println("----------");
}
}
}
}
class MyThread extends Thread {
private boolean flag = false;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println("flag = true");
}
}
这段代码的运行结果是
flag已经变为true了,但是main方法里的还是没有进入if。为什么会出现这样的结果呢?
这是因为在子线程中修改了flag,但main线程已经在子线程修改flag之前获取了flag的值为false,即时后面子线程改了flag,main线程也没有时间去同步最新的数据。
这种情况也成为并发编程下,多线程访问变量的不可见性。即多个线程访问共享变量,会出现一个线程修改变量的值后,其它线程看不到最新值得情况。
private volatile boolean flag = false;
使用volatile修饰后,可以实现并发下共享变量的可见性。一旦一个线程修改了volatile修饰的变量后,另一个线程可以立即获取到最新值。
上述问题通过加synchronized关键字也可以解决。
while (true) {
synchronized (myThread){
if(myThread.isFlag()){
System.out.println("----------");
}
}
}
main线程进入synchronized代码块的执行过程如下:
1.线程获取锁,2.清空工作内存,3.从主内存拷贝共享变量最新之到工作内存成为副本,4.执行代码,5.将修改后的副本值刷新回主内存中,6.释放锁。