Java中和线程相关的关键字就两:volatilesynchronized

volatile以前用得较少,以后会用得更少(后面解释)。它是一种非常轻量级的同步机制,它的三大特性是:

1、保证可见性,即强制将CPU高速缓存的数据立即写入主存,会导致其他CPU核中对应的高速缓存内容无效,就像这样:

如果由于同步需要,某行代码使用了volatile关键字,那么当CPU内核1收到指令时,会立即将它位于高速缓存中的数据(这里是字符串“1”)写到主存中去,那么其余的数据(“哈哈”、“test”、9.9)会全部失效。

2、有序性(禁止指令重排)

所谓指令重排,就是对于int a = 0、 int b = 1这类赋值语句,编译成class字节码时,a = 0,b = 1的顺序可能会因为编译器的优化而导致和书写时的顺序不一致。但如果有c = a + b,那么这行代码必须在前两句的后面执行。这个不用深究,知道就好了。

3、不保证原子性

所谓不保证原子性,就是如果遇到多个线程同时执行i = i + 1,那么i可能就不能保证仅仅被加1了。

根据Java的内存模型整理出如下这些规则,看看就好,能理解就记住,理解不了也没关系,不用记:

下面还是以代码来说明:

public class VolatileTest extends Thread {
static boolean flag = true; @Override
public void run() {
while (flag) {
}
System.out.println("子线程结束");
} public static void main(String[] args) throws InterruptedException {
new Thread(new VolatileTest()).start();
TimeUnit.SECONDS.sleep(1);
flag = false;
System.out.println("主线程结束");
}
}

加上volatile之后,子线程就不会一直卡住了:

public class VolatileTest2 extends Thread {
volatile static boolean flag = true; @Override
public void run() {
while (flag) {
}
System.out.println("子线程结束");
} public static void main(String[] args) throws InterruptedException {
new Thread(new VolatileTest2()).start();
TimeUnit.SECONDS.sleep(1);
flag = false;
System.out.println("主线程结束");
}
}

其实在第一个类VolatileTest中,即使不用volatile关键字,也能让程序正常结束,只需要添加一行代码就行了:

public class VolatileTest3 extends Thread {
static boolean flag = true; @Override
public void run() {
while (flag) {
// 只要在这里随便加一行代码,虽然原理不同,但效果和加了volatile一样
System.out.println(" ");
}
System.out.println("子线程结束");
} public static void main(String[] args) throws InterruptedException {
new Thread(new VolatileTest3()).start();
TimeUnit.SECONDS.sleep(1);
flag = false;
System.out.println("主线程结束");
}
}

总结一下,适应volatile的条件:

1、对变量的写操作不依赖于当前值,就是不会出现 x++ 或 x = x + y 这样的自增性质的语句;

2、变量没有包含在具有其他变量的代码中,就是不会出现 if(x > y) { // dosomething; } 这样的语句,因为这样的话,dosomething就不是线程安全的。

所以适合使用volatile的场景是:

1、状态标记量

2、双重检查

状态标记量:只设置简单的状态值,希望立即看到,且无其他变量参与。之前Volatile2中的flag就是这样的状态标记量。

双重检查(这个有点生僻冷门,不深究)。

因为目前synchronized的性能已经有了大幅提升,而且机器硬件的性能也较之前翻了几十倍,volatile存在的意义已经不大了。所以如果对volatile的理解不够深入,就干脆不用理解了。


再来看另一个重量级选手:synchronized

synchronized关键字用于解决多个线程之间的资源共享问题,通常有三大作用域:

1、静态方法:public synchronized static void methodName(),进入方法前要获得当前类对象的锁;

2、实例方法:public synchronized void methodName(),进入方法前要获得当前对象实例的锁;

3、代码块:最常用,指定一个特别的加锁对象,进入同步代码块前要获得这个对象锁。

这几个就不一个个地说了,因为网上这类例子太多了。

就简单说一下volatile和synchronized的比较:

1、volatile是synchronized的轻量级实现,性能要比synchronized要好;

2、volatile只能修饰变量,synchronized只能修饰方法和代码块;

3、volatile不会造成阻塞,synchronized会。

最后再说一个我之前遇到的小问题:

如果使用的IDE是Eclipse,那么当前活动的线程数量可能是1;

但如果使用的IDE是IDEA,那么当前活动的线程数量可能是2;

有图为证:

这是因为在IDEA中多了一个Monitor Ctrl-Break。

Java多线程-线程关键字(二)的更多相关文章

  1. Java多线程学习(二)synchronized关键字(2)

    转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79670775 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...

  2. Java多线程学习(二)synchronized关键字(1)

    转载请备注地址: https://blog.csdn.net/qq_34337272/article/details/79655194 Java多线程学习(二)将分为两篇文章介绍synchronize ...

  3. “全栈2019”Java多线程第二十二章:饥饿线程(Starvation)详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  4. “全栈2019”Java多线程第十二章:后台线程setDaemon()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  5. java 多线程—— 线程让步

    java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...

  6. java 多线程—— 线程等待与唤醒

    java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...

  7. Java 多线程 —— synchronized关键字

    java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...

  8. Java多线程--线程及相关的Java API

    Java多线程--线程及相关的Java API 线程与进程 进程是线程的容器,程序是指令.数据的组织形式,进程是程序的实体. 一个进程中可以容纳若干个线程,线程是轻量级的进程,是程序执行的最小单位.我 ...

  9. Java 多线程 - synchronize 关键字

    目录 Java 多线程 - synchronize 关键字 Java 多线程 - synchronize 关键字 学习自 http://cmsblogs.com/?p=2071 https://www ...

随机推荐

  1. 「SHOI2014」概率充电器

    题面 n <= 500000   0<= p,qi <= 100 题解 这是道概率树形DP题,但是很难推怎么用加法原理和乘法原理正向求每个点被充电的概率,所以我们求每个点不被充电的概 ...

  2. Maven中使用ssm框架出现:org.apache.tomcat.util.modeler.BaseModelMBean.invoke 调用方法[manageApp]时发生异常

    org.apache.tomcat.util.modeler.BaseModelMBean.invoke 调用方法[manageApp]时发生异常 首先可以排查一下像: @RequestMapping ...

  3. MQ系列4:NameServer 原理解析

    MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 1 关于NameServer 上一节的 MQ系列3:RocketMQ 架构分析,我们大致介绍了 ...

  4. iOS 组件化及二进制化的探索

    组件化的优缺点 组件化的拆分 组件与组件之间如何进行通讯(路由) 从Cocopods拉取代码的过程 远程索引库里很多的.spec文件,该文件记录了很多内容,如用户名,框架名称,描述,框架的地址 Pod ...

  5. 面试突击84:Spring 有几种事务隔离级别?

    Spring 中的事务隔离级别和数据库中的事务隔离级别稍有不同,以 MySQL 为例,MySQL 的 InnoDB 引擎中的事务隔离级别有 4 种,而 Spring 中却包含了 5 种事务隔离级别. ...

  6. Python数据科学手册-机器学习: 主成分分析

    PCA principal component analysis 主成分分析是一个快速灵活的数据降维无监督方法, 可视化一个包含200个数据点的二维数据集 x 和 y有线性关系,无监督学习希望探索x值 ...

  7. 7、System类

    System类 常见方法 exit 退出当前程序 System.out.println("ok1"); //1. exit(0) 表示程序退出 //2. 0 表示一个状态,正常的状 ...

  8. .env[mode]文件中如何添加注释

    前言 Vue-Cli 允许我们在项目根目录创建.env.[mode]文件来设置一些打包编译的启动参数,通过执行脚本的时候加mode参数,指定不同环境需要加载的配置文件 形如: .env.prod NO ...

  9. MinIO Client完全指南

    官方文档地址:http://docs.minio.org.cn/docs/master/minio-client-complete-guide 下载,添加云存储服务参考这篇文章:https://www ...

  10. k8s中的ingress使用上层负载均衡进行设置访问

    注意:这种情况下需要有个前提条件,也就是ingress-nginx-controller安装后的service是NodePort或者hostNetwork模式,而不能是ClusterIP,因为负载均衡 ...