前言

首先,我们使用多线程的目的在于提高程序的效率,但是如果使用不当,不仅不能提高效率,反而会使程序的性能更低,因为多线程涉及到线程之间的调度、CPU上下文的切换以及包括线程的创建、销毁和同步等等,开销比单线程大,因此需谨慎使用多线程。

在jdk1.5以后,提供了一个强大的java.util.concurrent包,这个包中提供了大量的应用于线程的工具类。

下面开始介绍volatile关键字和内存可见性,虽然volatile是在jdk1.5之前就有的,但还是想放在这里讲一下。

举例说明

首先,我们先看一段小程序。

 package com.ccfdod.juc;

 public class TestVolatile {
public static void main(String[] args) {
ThreadDemo td = new ThreadDemo();
new Thread(td).start(); while(true) {
if (td.isFlag()) {
System.out.println("--------------");
break;
}
}
}
} class ThreadDemo implements Runnable {
private boolean flag = false; @Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println("flag = " + isFlag());
} public boolean isFlag() {
return flag;
} public void setFlag(boolean flag) {
this.flag = flag;
}
}

程序运行结果:

flag = true

并且程序不会停止。

按理来说,应该会在td线程修改flag值后,主线程会打印出“--------------”,但是为什么没有出现预期效果呢?下面来分析这段程序,涉及到内存可见性问题。

内存可见性问题

当程序运行时,JVM会为每一个执行任务的线程分配一个独立的缓存空间,用于提高效率。

不难理解,程序开始执行时,由于线程td修改flag操作之前,sleep了200ms,td线程和main线程获取到的flag都为false,但为什么td线程将flag改为true后,main线程没有打印出“--------------”呢?原因在于:while(true)是执行效率很高,使得main线程没有时间再次从主存中获取flag的值,因此程序在td线程将flag修改为true后,没有停止运行的原因。其实在while(true)后面稍微延迟一点(比如说,打印一句话),都会使main线程将主存中的flag=true读取。

产生这种情况的原因就在于,两个线程在操作共享数据时,对共享数据的操作是彼此不可见的。

那么为了不让这种问题出现,怎么解决呢?

一、使用synchronized同步锁

while(true) {
synchronized (td) {
if (td.isFlag()) {
System.out.println("--------------");
break;
}
}
}

使用synchronized同步锁能保证数据的及时更新。但是效率太低。

二、使用volatile关键字

当多个线程进行操作共享数据时,可以保证内存中的数据可见。底层原理:内存栅栏。使用volatile关键字修饰时,可理解为对数据的操作都在主存中进行。

private volatile boolean flag = false;

相较于synchronized是一种较为轻量级的同步策略。

注意:

  • volatile不具备“互斥性”
  • volatile不能保证变量的“原子性”

关于“原子性”的问题将在下一篇博客中讨论。

最新文章

  1. Linux 使用iostat分析IO性能
  2. LUA之面向对象
  3. Android monkey介绍
  4. 解决方案:Error:Execution failed for task ':app:compileDebugAidl'. > aidl is missing
  5. WCF 服务的ABC之契约(七)
  6. ios8,xcode6 周边
  7. usaco 猜数游戏
  8. linux网络编程--跳水send和recv
  9. Malloc碎碎念
  10. jQuery本身方法($.each,$.map,$.contains,$ajax)
  11. String是值传递还是引用传递
  12. 计算机17-3,4作业F
  13. python第十二天, 三元表达式, 函数对象,名称空间与作用域,函数的嵌套定义
  14. org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): cn.e3mall.search.mapper.ItemMapper.getItemList
  15. WPF常用布局介绍
  16. robotframework+selenium搭配chrome浏览器,web测试案例(搭建篇)
  17. php 文件上传 $_FILES 错误码
  18. ThinkingInJava 学习 之 0000003 控制执行流程
  19. python字典的基本操作
  20. 《linux内核设计与实现》第二章

热门文章

  1. Pandas 如何去除、取消已经设置好的索引
  2. django自带过滤器大全
  3. Python高级教程-sorted
  4. Sql Server 中 GAM、SGAM、PAM、IAM、DCM 和 BCM 的详解与区别
  5. Cocos2dx3.1-Android环境搭建初体验
  6. keras中 LSTM 的 [samples, time_steps, features] 最终解释
  7. mysql内置数据库
  8. 扯一扯 C#委托和事件?策略模式?接口回调?
  9. poj2993
  10. 【Java Web】把逻辑名映射到servlet文件