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. MySQL主从-报错

    安装: 环境: 系统:centos7 x3 master:192.168.220.133 slave1:192.168.220.136 slave2:192.168.220.137 mysql服务器, ...

  2. Spring 04: IOC控制反转 + DI依赖注入

    Spring中的IOC 一种思想,两种实现方式 IOC (Inversion of Control):控制反转,是一种概念和思想,指由Spring容器完成对象创建和依赖注入 核心业务:(a)对象的创建 ...

  3. 基于ASP.NET Core 6.0的整洁架构

    大家好,我是张飞洪,感谢您的阅读,我会不定期和你分享学习心得,希望我的文章能成为你成长路上的垫脚石,让我们一起精进. 本节将介绍基于ASP.NET Core的整洁架构的设计理念,同时基于理论落地的代码 ...

  4. Android下的IPC通信方式

    一.Bundle Android的Activity.Service.Receiver都支持在Intent传递Bundle数据,Bundle实现了Parcelable接口, 所以能很方便的在不同进程之间 ...

  5. Svelte Ui Admin后台管理系统|svelte3+svelteUI中后台前端解决方案

    基于svelte3.x+svelteKit+svelte-ui网页后台管理系统SvelteAdmin. Svelte-Ui-Admin 基于svelte3.x+svelteKit+vite3+echa ...

  6. 详解字符编码与 Unicode

    人类交流使用 A.B.C.中 等字符,但计算机只认识 0 和 1.因此,就需要将人类的字符,转换成计算机认识的二进制编码.这个过程就是字符编码. ASCII 最简单.常用的字符编码就是 ASCII(A ...

  7. 基于HBuilderX+UniApp+ThorUI的手机端前端的页面组件化开发经验

    现在的很多程序应用,基本上都是需要多端覆盖,因此基于一个Web API的后端接口,来构建多端应用,如微信.H5.APP.WInForm.BS的Web管理端等都是常见的应用.本篇随笔继续分析总结一下项目 ...

  8. ProxySQL 密码管理

    ProxySQL是一个协议感知的proxy.由于ProxySQL基于流量进行路由,当一个客户端连接ProxySQL时,它还无法识别它的目标主机组,因此ProxySQL需要对该客户端进行认证.基于此,需 ...

  9. 14. 第十三篇 二进制安装kube-proxy

    文章转载自:https://mp.weixin.qq.com/s?__biz=MzI1MDgwNzQ1MQ==&mid=2247484231&idx=1&sn=9e722bee ...

  10. Elasticsearch:Elasticsearch中的refresh和flush操作指南

    在今天的文章里,我们来主要介绍一下Elasticsearch的refresh及flush两种操作的区别.如果我们从字面的意思上讲,好像都是刷新的意思.但是在Elasticsearch中,这两种操作是有 ...