Java内存模型(Java Memory Model)描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这样的底层细节!
- 所有的变量都存储在主内存中
- 每个线程都有自己独立的工作内存,里面保存该线程使用到的变量副本(主内存中该变量的一份拷贝)
下面的代码期望运行结果是500!但实际结果几乎都不是!
public class VolatileDemo {
private int number = 0;
public int getNumber() {
return this.number;
}
public void increase() {
try
{
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.number++;
}
public static void main(String[] args) {
final VolatileDemo volatileDemo = new VolatileDemo();
//启动500个线程,对volatileDemo对象调用increase方法!
for(int i = 0; i < 500; i++)
{
new Thread(new Runnable()
{
@Override
public void run()
{
volatileDemo.increase();
}
}).start();
}
//如果有子线程在运行,主线程就让出cpu资源,直到子线程都运行完,主线程再继续执行!
while(Thread.activeCount() > 1)
{
Thread.yield();
}
System.out.println("number: " + volatileDemo.getNumber());
}
}
程序分析:
1 线程A读取number的值。
2 线程B读取number的值
3 线程B执行加1操作
4 线程B写入最新的number值
5 线程A执行加1操作
6 线程A写入最先的number值
结果:两次number++实际只增加了1.
解决方案:
保证number自增操作的原子性:
1 使用synchronized关键字
2 使用ReentrantLock (java.util.concurrent.locks包下)
3 使用AtomicInteger(java.util.concurrent.atomic包下)
public void increase() {
try
{
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (this) {
this.number++;
}
}
private Lock lock = new ReentrantLock();
private int number = 0;
public int getNumber() {
return this.number;
}
public void increase() {
try
{
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
try {
this.number++;
} finally {
lock.unlock();
}
}