千家信息网

Java线程中关键字和方法的示例分析

发表于:2025-01-17 作者:千家信息网编辑
千家信息网最后更新 2025年01月17日,这篇文章主要为大家展示了"Java线程中关键字和方法的示例分析",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"Java线程中关键字和方法的示例分析"这篇文章
千家信息网最后更新 2025年01月17日Java线程中关键字和方法的示例分析

这篇文章主要为大家展示了"Java线程中关键字和方法的示例分析",内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下"Java线程中关键字和方法的示例分析"这篇文章吧。

一、volatile关键字

1,volatile 能保证内存可见性

代码在写入 volatile 修饰的变量的时候

改变线程工作内存中volatile变量副本的值

将改变后的副本的值从工作内存刷新到主内存

代码在读取 volatile 修饰的变量的时候

从主内存中读取volatile变量的最新值到线程的工作内存中

从工作内存中读取volatile变量的副本

 static class Counter{        public int flag = 0;    }        public static void main(String[] args) {        Counter counter = new Counter();        Thread t1 = new Thread(){            @Override            public void run() {                while (counter.flag == 0){                 }                System.out.println("循环结束");            }        };        t1.start();        Thread t2 = new Thread(){                Scanner scanner = new Scanner(System.in);                System.out.println("请输入一个整数:");                counter.flag = scanner.nextInt();        t2.start();

预期的结果是:

线程1会先进入循环状态,线程2读取一个用户输入的整数。随着用户的输入一个非0的整数之后,线程1就会终止。

实际效果:

线程2输入完毕后,线程1循环并未结束

2,编译器优化问题

线程1的核心代码中,循环其实啥也没干,反复快速的执行循环条件中的比较操作。

先从内存中读取flag的值到CPU中

在CPU中比较这个值和0的关系

编译器判定这个逻辑中循环没有干啥事,只是频繁的读取内存而已,于是编译器就把读内存的操作优化了,第一次把内存中的数据督读到CPU之后,后序的内存并不是真正的从内存中读,而是直接从刚在的CPU中读数据

编译器认为flag没有改动,其实只是在当前线程中没有改动,编译器就不能感知到其他的线程对flag进行了修改

static class Counter{        public  volatile  int flag = 0;    }     public static void main(String[] args) {        Counter counter = new Counter();        Thread t1 = new Thread(){            @Override            public void run() {                while (counter.flag == 0){                }                System.out.println("循环结束");            }        };        t1.start();        Thread t2 = new Thread(){                Scanner scanner = new Scanner(System.in);                System.out.println("请输入一个整数:");                counter.flag = scanner.nextInt();        t2.start();

加了volatile之后,对这个内存的读取操作肯定是从内存中来读

不加volatile的时候,读取操作可能不是从内存中读取,从CPU上读取旧值,这都是不确定的

volatile 和 synchronized 有着本质的区别.

synchronized 能够保证原子性, volatile 保证的是内存可见性

synchronized 既能保证原子性, 也能保证内存可见性.

二、wait 和 notify

由于线程之间是抢占式执行的, 因此线程之间执行的先后顺序难以预知. 但是实际开发中有时候我们希望合理的协调多个线程之间的执行先后顺序

1,wait()方法

wait 做的事情:

使当前执行代码的线程进行等待. (把线程放到等待队列中)

释放当前的锁

满足一定条件时被唤醒, 重新尝试获取这个锁.

wait 要搭配 synchronized 来使用. 脱离 synchronized 使用 wait 会直接抛出异常.

wait 结束等待的条件:

其他线程调用该对象的 notify 方法.

wait 等待时间超时 (wait 方法提供一个带有 timeout 参数的版本, 来指定等待时间).

其他线程调用该等待线程的 interrupted 方法, 导致 wait 抛出 InterruptedException 异常.

public static void main(String[] args) throws InterruptedException {        Object object = new Object();        synchronized (object){            System.out.println("等待前");            object.wait();            System.out.println("等待后");        }    }

就相当于一个人去ATM上取钱,发现ATM中没钱,然后阻塞等待(不参与后续锁的竞争)——wait

等到银行的工作人员来送钱,你可以取钱了——notify

2,notify()方法

notify 方法是唤醒等待的线程.

方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。

如果有多个线程等待,则有线程调度器随机挑选出一个呈 wait 状态的线程。(并没有 "先来后到")

在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行 完,也就是退出同步代码块之后才会释放对象锁。

 public static void main(String[] args) {        Object locker = new Object();         Thread t1 = new Thread(){            @Override            public void run() {                synchronized (locker){                    while (true){                        System.out.println("wait开始");                        try {                            locker.wait();                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                        System.out.println("wait结束");                    }                }            }        };        t1.start();        Thread t2 = new Thread(){                Scanner scanner = new Scanner(System.in);                System.out.println("输入一个整数,继续执行");                int num = scanner.nextInt();                    System.out.println("notify开始");                    locker.notify();                    System.out.println("notify结束");        t2.start();    }

3,notifyAll()方法

notify方法只是唤醒某一个等待线程.

使用notifyAll方法可以一次唤醒所有的等待线程.

以上是"Java线程中关键字和方法的示例分析"这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!

线程 内存 方法 对象 循环 输入 代码 变量 整数 编译器 保证 工作 编译 关键 关键字 示例 分析 之间 内容 副本 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 梦幻西游可转入服务器 网络安全打分表 苏州市蓝水软件开发公司 软件开发公司哪家比较好 互联网科技知识大全 软件开发面试要准备什么东西 远程桌面连接服务器显示程序运行 内网网络安全注意事项 完成网络安全监测装置部署 互联网软件开发业务流程 bmob保存数据库 第二批国家基层网络安全员 云数据库怎么保障安全性 计算机和网络技术基础课程 java数据库自增长单词 pubg体验服服务器无响应 互联网高科技与市场监督的联系 方舟生存进化服务器里面有什么 手机云服务器流量不够用 电信集成郑州软件开发中心 联想服务器管理口连接不上 关于北京冬奥会网络安全工作总结 全文数据库建立要本着什么原则 ps4服务器 传奇世界h5数据库怎么用 威海软件开发哪家强 godex数据库错误 软件开发行业经济分析 华为服务器app怎么下载 安卓集成的数据库在哪
0