发布于2021-06-12 15:00 阅读(126) 评论(0) 点赞(10) 收藏(4)
目录
1) 使用标志位的方式
isStop变量.
- public class FlagStop {
- private static volatile boolean isStop;
-
- public static void main(String[] args) throws InterruptedException {
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- while (!isStop) {
- System.out.println("转账....");
- Thread.sleep(1000);
- }
- }catch (InterruptedException e){
- e.printStackTrace();
- }
- }
- }).start();
- //停止转账
- Thread.sleep(3000);
- isStop=true;
- }
- }
2) 线程中断的API
前置: 线程Thread中,存在一个中断标志位,默认值=false(没有被中断)
- public class InterruptThread {
- public static void main(String[] args) throws InterruptedException {
- Thread t=new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- while(!Thread.currentThread().isInterrupted()){
- System.out.println("转账...");
- Thread.sleep(1000);
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- });
- t.start();
- Thread.sleep(3000);
- t.interrupt();
- }
- }
1) API
线程引用对象.join() / 线程引用对象.join(long)
当前线程阻塞等待,直到满足以下条件:
① 无参: 线程引用执行完毕
② 有参: 等待指定时间,或者线程引用执行完毕,任意一个条件满足,当前线程继续向下执行.
2) 示例
- public class LearnJoin {
- public static void main(String[] args) throws InterruptedException {
- Thread t=new Thread(new Runnable() {
- @Override
- public void run() {
- try{
- System.out.println("t start");
- Thread.sleep(5000);
- System.out.println("t end");
- }catch(InterruptedException e){
- e.printStackTrace();
- }
- }
- });
- t.start();
- t.join(2000);
- System.out.println("main");
- }
- }
1) API
参数为:
true: 设置为守护线程;
false: 设置为用户线程.
2) 一个Java进程,至少有一个用户线程存在,才会运行,否则就结束.
这里的等待,超时等待,阻塞状态,和之前学习的进程状态转换图,阻塞状态,表现是一样的,线程暂停/挂起.
代码层面上: 多个线程对同一个共享数据的操作(读,写),如果有写操作,就存在线程安全问题.
底层的原因: 原子性,可见性,有序性.
① 原子性
特殊的非原子性操作:
a) n++,n--,++n,--n
从主存把数据读取到CPU (读)
进行数据的更新 (改)
把数据写回到主存 (写回)
b) new一个对象
一行new对象的代码,分解为:
I. 分配内存空间(Java虚拟机)
II. 执行构造方法(字节码层面的<init>构造方法: 收集成员变量,实例代码块,Java代码中的构造方法)
III. 把new的对象赋值给变量
② 可见性
线程对主存数据的操作,需要先加载到线程私有的工作内存,操作完再写回到主存,线程之间的工作内存,是互相不可见的.
共享变量,存储于主存(线程共享区域),线程对变量的操作:
a) 加载到工作内存
b) 操作(赋值,修改)
c) 写回主存
③ 重排序
多行指令(字节码指令在Java虚拟机执行,及机器码指令在CPU执行)在执行时,可能进行优化(目的是提高执行效率),只是不能重排序有关联的指令.
比如洗苹果,吃苹果,这两条指令由于具有前后关系,所以就不能重排序.洗苹果,洗梨,就可以进行重排序.
线程安全,是由于多个线程对共享变量的操作,并发并行执行的结果.(共享变量称为临界资源,这种代码(多行),称为临界区)
思路: 临界区代码执行时,先申请jvm加锁,然后再执行. 申请锁,是需要同一把锁来保证线程安全;申请失败,线程则需要等待(可以是阻塞式的,也可以是非阻塞式)
这种操作,在多个线程执行临界区都申请同一把锁的情况下,多个线程运行的结果,就表现为: 多个线程,依次执行临界区代码.
假如现在我(线程)----申请把餐厅101包间加锁---吃饭,喝水(临界区: 此时作为不可分割的最小执行单位)----归还锁---通知其他要在101做事的人(其他线程)---重复以上流程.
1) 作用:
基于对象头加锁的方式,只要申请同一把锁加锁的线程,都有同步互斥的作用.
2) 语法:
语法上对应申请锁-----加锁----释放锁
synchronized代码行: 线程申请某个对象锁
synchronized作用域结束(花括号结束): 自动释放对象锁.
3) 特别注意的事项:
多线程只有申请同一个对象的加锁,才具有同步互斥的效果.
不是对同一个对象加锁,并发并行的执行.(部分A加同一把锁,部分B不加锁,A中的多个线程同步互斥,但是B和A,B内部的多个线程,都是并发并行)
1) 作用
①保证变量(分解为字节码以后的)的可见性.
②建立一个内存屏障,禁止指令重排序.
2) 使用场景:
代码行本身保证原子性的前提下,变量使用volatile修饰,可以保证线程安全.
关于代码行本身保证原子性的理解:
①读(从主存读取到线程的工作内存)
②修改为一个常量值(把常量值写回主存)
原文链接:https://blog.csdn.net/weixin_43939602/article/details/117441171
作者:狗蛋来了
链接:http://www.javaheidong.com/blog/article/222576/7e8931a996477802c724/
来源:java黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!