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. GitHub Pages 站点建设

    1.简介 GitHub Pages 是通过 GitHub 托管和发布的公共网页,将纯文本转换为静态博客网站. 您可以使用 GitHub Pages 来展示一些开源项目.博客甚或分享您的简历,有内存限制 ...

  2. 原生应用、Web 应用和混合应用的概念,以及 Flutter 技术是什么

    应用类型 原生应用 原生应用就是使用特定的语言开发的应用,例如 Android 原生应用.Windows 原生应用.iOS 原生应用等.开发 Android 原生应用时,其特定的编程语言为 Java ...

  3. 刷题记录:Codeforces Round #719 (Div. 3)

    Codeforces Round #719 (Div. 3) 20210703.网址:https://codeforces.com/contest/1520. 没错,我是个做div3的蒟蒻-- A 大 ...

  4. React报错之Parameter 'event' implicitly has an 'any' type

    正文从这开始~ 总览 当我们不在事件处理函数中为事件声明类型时,会产生"Parameter 'event' implicitly has an 'any' type"错误.为了解决 ...

  5. 部署k8s的heapster监控

    Heapster是容器集群监控和性能分析工具,天然的支持Kubernetes和CoreOS heapster监控目前官网已经不更新,部署学习使用 heapster: 收集监控数据 influxdb:数 ...

  6. scp复制发送文件夹到其他服务器上

    简述scp: scp是secure copy的简写,是linux系统下基于ssh登陆进行安全的远程文件拷贝命令. 写法: scp [可选参数] 登录名@地址:源路径  目标路径. 举例:scp -r ...

  7. EL&JSTL笔记------jsp

    今日内容 1. JSP: 1. 指令 2. 注释 3. 内置对象 2. MVC开发模式 3. EL表达式 4. JSTL标签 5. 三层架构 JSP: 1. 指令 * 作用:用于配置JSP页面,导入资 ...

  8. 第四章 部署K8s前准备工作

    一.主机准备 1.硬件 准备5台2C/2g/50g虚拟机: Centos7.6系统 2.集群规划 使用10.4.7.0/24网络 IP 主机名 10.4.7.11 hdss7-11.host.com ...

  9. KingbaseES R3集群备库执行sys_backup.sh物理备份案例

    案例说明: KingbaseES R3的后期版本支持通过sys_backup.sh执行sys_rman的物理备份,实际上是调用了sys_rman_v6的工具做物理备份.本案例是在备库上执行集群的备份, ...

  10. ASP.NET MVC 对于视图引擎的优化

    我经常使用asp.net MVC框架来做网站.总的来说,MVC框架是一个非常优秀的框架.相对于曾经的web form模式,我个人感觉分工更加合理思路也更加清晰,但是交给开发人员的工作也相对变多了. 当 ...