关于线程的缓存刷新

这篇具有很好参考价值的文章主要介绍了关于线程的缓存刷新。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

今天又是摸鱼的一天,在群里闲聊的时候突然有位群友题了个问题:

关于线程的缓存刷新

群友们反应很快,一下子就解决了没有加关键字volatile,不能保证可见性。所以循环线程中的kaiguan变量一直读取的是线程缓存中的值,而线程缓存的值一直无法读取到主内存中的值。

当然这个问题难不倒这位提问的群友,所以他又提问了。
关于线程的缓存刷新

除了加volatile之外,还有没有其他方式?

正好闲着无聊,我便开始试着解决这个问题。

public class test2 {

    private static boolean kaiguan =true;
    private static void go(){
        new Thread(()->{
            while (kaiguan){
            }

            System.out.println("事情做完了");
        }).start();
        new Thread(()->{
            try {
                Thread.sleep(1000*3);
                kaiguan=false;
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }).start();
    }

    public static void main(String[] args) {
        go();
    }


}

首先我问了ChatGPT

如果不使用 volatile 关键字,也可以使用其他的多线程机制来保证可见性,例如使用 AtomicBoolean 类,这个类提供了 get()set() 方法,可以保证操作的原子性和可见性。
修改后的代码如下所示:
private static AtomicBoolean kaiguan = new AtomicBoolean(true);
private static void go() {
new Thread(() -> {
while (kaiguan.get()) {
Thread.yield(); // 或者使用Thread.sleep(),暂停线程一段时间
}
System.out.println("事情做完了");
}).start();
new Thread(() -> {
try {
Thread.sleep(1000 * 3);
kaiguan.set(false); // 设置kaiguan变量为false,退出循环
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
public static void main(String[] args) {
go();
}
在上述代码中,我们使用了 AtomicBoolean 类来代替原来的 boolean 类型的 kaiguan 变量,并在 while 循环中使用了 kaiguan.get() 方法来获取 kaiguan 变量的值,从而保证可见性。在另一个线程中,我们使用 kaiguan.set(false) 方法将 kaiguan 变量设置为 false ,从而退出循环。

可以看到 除了使用volatile 来声明变量,我们还可以用AtomicBoolean 来声明kaiguan。同样能保证可见性。

那如果不用AtomicBoolean声明kaiguan呢?

我看到了下面这篇文章:

线程的缓存何时刷新? - 简书 (jianshu.com)

在Doug Lea大神的Concurrent Programming in Java一书中有这样一个片段来描述synchronized这个关键字:

In essence, releasing a lock forces a flush of all writes from working memory employed by the thread, and acquiring a lock forces a (re)load of the values of accessible fields. While lock actions provide exclusion only for the operations performed within a synchronized method or block, these memory effects are defined to cover all fields used by the thread performing the action.

简单翻译一下:从本质上来说,当线程释放一个锁时会强制性的将工作内存中之前所有的写操作都刷新到主内存中去,而获取一个锁则会强制性的加载可访问到的值到线程工作内存中来。虽然锁操作只对同步方法和同步代码块这一块起到作用,但是影响的却是线程执行操作所使用的所有字段。

也就是说我们可以用加锁来解决线程刷新这个问题。

所以我们可以手动加上System.out.println();来退出该循环。

因为System.out.println();底层是加锁的

    public void println() {
        newLine();
    }
    
        private void newLine() {
        try {
            synchronized (this) {
                ensureOpen();
                textOut.newLine();
                textOut.flushBuffer();
                charOut.flushBuffer();
                if (autoFlush)
                    out.flush();
            }
        }
        catch (InterruptedIOException x) {
            Thread.currentThread().interrupt();
        }
        catch (IOException x) {
            trouble = true;
        }
    }
    

我们在看看chatgpt的回答,既然里面有提到yield()与sleep(),那么我们就试试

Thread.yield();
与
Thread.sleep(0);

发现果然成功跳出循环,那么yield()与sleep(0)到底发生了什么导致缓存刷新呢?

没错就是上下文切换!

yield()与sleep(0)会导致上下文切换,从而导致缓存失效,从而拉去主内存中的新值。

当然我们也可以直接使用Unsafe方法中的loadFence()方法。

使用UnsafeFactory.getUnsafe().loadFence();也同样可以跳出循环,因为loadFence: 可以保证在这个屏障之前的所有读操作都已经完成。

Unsafe需要我们通过反射获取,直接调用会报错:

  public static Unsafe getUnsafe() {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            return (Unsafe) field.get(null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

在寻找其他答案的过程中我还发现final关键字同样也有刷新缓存的作用

首先自定义一个类,将其中的num设置为final


public  class TestTempOne {
  final int num; //设为final;
    TestTempOne(int num){
        this.num=num;
    }

}

private static void go(){
        new Thread(()->{
            while (kaiguan){
//                new TestTempOne(0);  //跳出循环
//               new String("a");      //跳出循环
//                new Integer(0);      //死循环
//                new Integer(129); //死循环
                new Integer(100000000); //死循环
            }

可以看到最终会跳出循环,但是有个问题Integer中value的值同样也是final。但却不能刷新缓存。而String则是一个final char数组,也可以跳出循环。目前没有找到答案,如果有大佬知道答案,请告知我一下!

总结:我目前知道的有六种:

1、使用volatile

2、使用synchronized或者Lock

3、使用AtomicBoolean

4、使用UnsafeFactory.getUnsafe().loadFence();

5、使用yield()与sleep()

6、final关键字文章来源地址https://www.toymoban.com/news/detail-455281.html

到了这里,关于关于线程的缓存刷新的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 电脑怎么刷新本地dns缓存?刷新dns缓存的命令介绍

    电脑怎么进行刷新DNS缓存呢? 下面简单介绍下。  1、开始菜单,点击运行,并输入 cmd,打开命令窗口。  2、在命令窗口输入 ipconfig /displaydns 命令。 查看本机已经缓存的dns信息。 3、输入 ipconfig /flushdns  ,进行dns刷新,这样缓存就清除啦。 4、继续查看dns 信息,输入 ipcon

    2024年02月07日
    浏览(16)
  • 如何刷新dns缓存 刷新dns缓存命令(ipconfig /flushdns)

    刷新dns缓存的命令是什么 ?这是因为当我们需要安装新的软件的时候就需要先刷新下dns服务器,清除dns缓存。比如域名更改过需要重新解析新的域名,这样虽然DNS服务器上已经更新,但本地还有DNS缓存,造成还是老的IP地址,那么可能会出现一些未知的故障,这样我们应该通

    2024年02月05日
    浏览(36)
  • 浏览器刷新页面,缓存的处理方式,强制刷新

    刷新页面的缓存处理的方式对比 地址栏回车/直接访问 URL 保留强缓存,保留协商缓存,走正常请求流程 点击浏览器刷新按钮 忽略强缓存,保留协商缓存 按f5【command + r】 忽略强缓存,保留协商缓存 ctrl + f5 【command + shift + r 】 忽略强缓存,忽略协商缓存,从服务器端请求最

    2024年02月02日
    浏览(49)
  • 刷新DNS命令 如何刷新DNS缓存(flushdns)

    运行:ipconfig /displaydns这个命令,查看一下本机已经缓存了那些的dns信息的,然后输入下面的命令 ipconfig /flushdns 这时本机的dns缓存信息已经清空了,我们可以再次输入第一次输入的命令来看一下, ipconfig /displaydns ipconfig /displaydns显示dns缓存 ipconfig /flushdns 刷新DNS记录 ipconfig /renew重请

    2024年02月06日
    浏览(12)
  • MacOS刷新DNS缓存

    当你在浏览网站或者使用网络服务时,你的 MacOS 系统会自动保存(缓存)DNS 查询结果,以加速后续的访问。但是,有时候,DNS 缓存可能会造成问题,例如网站已经迁移到新的服务器,而你的系统仍然在尝试访问旧的地址。在这种情况下,你可能需要刷新(清空)你的 DNS 缓

    2024年02月20日
    浏览(11)
  • Linux 如何刷新 DNS 缓存

    Linux 如何刷新 DNS 缓存

    Linux 如何刷新 DNS 缓存 全文:如何刷新 DNS 缓存 (macOS, Linux, Windows) Unix Linux Windows 如何刷新 DNS 缓存 (macOS, FreeBSD, RHEL, CentOS, Debian, Ubuntu, Windows) 请访问原文链接:https://sysin.org/blog/how-to-flush-dns-cache/,查看最新版。原创作品,转载请保留出处。 作者主页:sysin.org 刷新或者清除

    2024年02月13日
    浏览(12)
  • MacOS13刷新DNS缓存

    当你在浏览网站或者使用网络服务时,你的 MacOS 系统会自动保存(缓存)DNS 查询结果,以加速后续的访问。但是,有时候,DNS 缓存可能会造成问题,例如网站已经迁移到新的服务器,而你的系统仍然在尝试访问旧的地址。在这种情况下,你可能需要刷新(清空)你的 DNS 缓

    2024年02月06日
    浏览(10)
  • Windows网络自学的第一天:创建线程

    Windows网络自学的第一天:创建线程

    目录 一、创建线程 CreateThread函数:  下面是示例: ​编辑 ThreadProc函数解释:     DWORD的本质是 unsigned long    PVOID的本质是 void* 二、线程的终止 1.WaitForSingleObject()函数: 示例如下: 2.ExitThread()函数: 示例如下: 3.TerminateThread()函数: 4.CloseHandle()函数: 5.正常return 0; 三、线程的

    2024年02月14日
    浏览(11)
  • 如何刷新 DNS 缓存 (macOS, Linux, Windows)

    如何刷新 DNS 缓存 (macOS, Linux, Windows)

    如何刷新 DNS 缓存 (macOS, Linux, Windows) Unix Linux Windows 如何刷新 DNS 缓存 (macOS, FreeBSD, RHEL, CentOS, Debian, Ubuntu, Windows) 请访问原文链接:https://sysin.org/blog/how-to-flush-dns-cache/,查看最新版。原创作品,转载请保留出处。 作者主页:sysin.org 刷新或者清除 DNS 缓存,通常是因为有过时的

    2024年02月10日
    浏览(9)
  • python--pyQt5 页面刷新\线程刷新\界面卡顿 --- 多线程处理(线程的开始/暂停/恢复/取消)同时运行两个不同的线程 pyside6

    python--pyQt5 页面刷新\线程刷新\界面卡顿 --- 多线程处理(线程的开始/暂停/恢复/取消)同时运行两个不同的线程 pyside6

    参考:https://blog.csdn.net/zx520113/article/details/86598658 PyQt5中使用QTimer定时刷新:当要执行可能会超过设定时间的代码 刷新界面命令:QApplication.processEvents() 对于执行很耗时的程序来说,由于PyQt需要等待程序执行完毕才能进行下一步,这个过程表现在界面上就是卡顿,而如果需要

    2024年03月25日
    浏览(11)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包