volatile 关键词 设置变量的可见性
线程A:
package demo; public class ThreadDemo implements Runnable { private boolean flag = false; public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public void run() { try { Thread.sleep(200); } catch (Exception e) { } flag = true; System.out.print("flag = " + isFlag()); } }
主线程:
package demo; public class TestVolatile { public static void main(String[] args) { ThreadDemo threadDemo = new ThreadDemo(); new Thread(threadDemo).start(); while (true) { if (threadDemo.isFlag()) { System.out.println("-------------------"); break; } } } }
运行结果:
程序进入死循环。原因:
CPU在读取数据值的时候会首先从缓存中读取数据。缓存:因为CPU的速度太快了内存跟不上,所以建立了缓冲区。
A线程虽然已经将flag的值更改了但是CPU不知道,主线程进入死循环。代码部分为了演示的原因让A线程休眠了200ms。我们将这个200ms去掉就会发现线程有时会进入死循环,有时会成功的运行。这是线程在抢占CPU。在未休眠的情况下,如果A线程先抢占CPU跑完代码那么主线程就不会进入死循环,同理主线程先运行,先进入循环那么就会直接死循环。
加上volatile关键字:
private volatile boolean flag = false;
主线程代码不变。
代码直接结束,原因:加上volatile后,对这个变量所有的读取操作都会直接读取内存,不会读取缓存。这就是我们所说的可见性。当时volatile只能解决可见性的问题,无法解决并发问题,并发问题还是要使用synchronized关键字。
在代码执行的时候会有指令重排序的问题,这个时候使用volatile修饰的变量,不会有指令重排序的情况发生。
ThreadLocal:SimpleDateFormat是并发不安全的 使用ThreadLocal就可以在并发的环境下安全了
package demo.local; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class LocalThread { private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } }; public String format(Date date) { return df.get().format(date); } public Date parse(String str) throws ParseException { return df.get().parse(str); } }ThreadLocal是为每一个线程拷贝了一份变量,这个变量是线程独有的,所以不会有线程安全的问题。
也没写什么就写了一点volatile和ThreadLocal用法和好处。
努力吧,皮卡丘。