Java线程要点

本文深入探讨Java中线程间变量访问的重新排序问题,解释了volatile关键字如何确保变量的可见性和有序性,以及其在多线程环境中防止数据竞争的关键作用。

1>

什么是单个线程中语句的重新排序

Ans。 编译器/ JVM对特定线程中的语句进行重新排序,以免影响单线程执行的代码语义。 例如,请参见以下代码。


class A{ 
    private int k = 0;
    private boolean l = false;
    public void example(int a, boolean b){
        k = a;//....  Line 1
        l = b;//....  Line 2 
    } 
    public void show(){
        System.out.println(k);//.... Line 3
        System.out.println(l);//.... Line 4
    } 
    public static void main(String args[]){ 
        A a = new A();
        a.example(3,"true");
        a.show(); 
    }
}  
方案1 :在这里,第1行和第2行的执行顺序并不重要。 因为他们彼此不依赖。 因此,它们可能会重新排序。 现在很正常的是,在主线程中,行的执行顺序为2、1、3、4。

所以输出将是

3

真正

但是方法show()中的执行顺序永远无法重新排序。 因为那样会改变方法的语义。 那么结果如下

真正

3

但这不是该程序的目的。 因此,这永远不会发生。

方案2 :但是,当两个线程一起工作时,方案可能会有所不同。 假设在主线程中启动了两个线程。 线程1执行方法a.example(..),线程2执行a.show()

此处的执行很可能按以下顺序进行。

2,3,4,1

现在输出如下

0

真正

这意味着k在l之前分配。

所以这不是我们想要的对吗?

2>

如何停止重新订购

Ans。 如果将变量k和l声明为volatile,则可以停止重新排序。

让我再次重写代码


class A{ 
    private volatile int k = 0;
    private volatile boolean l = false; 
    public boolean getL(){
        return l;
    }
    public void example(int a, boolean b){
        k = a;//....  Line 1
        l = b;//....  Line 2 
    } 
    public void show(){
        System.out.println(k);//.... Line 3
        System.out.println(l);//.... Line 4
    } 
    public static void main(String args[]){ 
        A a = new A(); 
        //code is omitted.
        /*a.example(3,true) is run in thread1*/
        /*a.show() is run in thread2*/ 
    }
} 
考虑场景2。在此重复:线程1执行a.example(),线程2执行a.show()。

现在一种可能的订购如下

1,3,4,2

然后输出如下

3

此处1在2之前执行。重新排序已停止。 因为如果变量是易失性的,则它们的访问顺序与编写代码的顺序相同。

但是我们想要这个吗? 我们需要查看分配的值3和“真”。

3>

怎么做呢?

在线程2的run方法中,我们可以添加此代码


if(a.getL()){
    a.show();
} 
这两个线程执行的行将按照以下顺序

1,2,3,4

同样的输出如下

3

真正

4>

现在是一个棘手的问题

假设我们有3个变量j,k和l。 我以以下形式写课


class A{ 
    private volatile int j = 0;
    private volatile int k = 0;
    private volatile boolean l = false;
    public void example(int a, boolean b){
        j = a;//....  Line 1
        k = a;//....  Line 2
        l = b;//....  Line 3 
    }
    public boolean getL(){
        return l;
    }
    public void show(){
        System.out.println(j)//...  Line 4
        System.out.println(k);//.... Line 5
        System.out.println(l);//.... Line 6
    } 
    public static void main(String args[]){ 
        A a = new A(); 
        //code is omitted.
        /*a.example(3,true) is run in thread1*/
        /*a.show() is run in thread2*/ 
    }
} 
因此,如果在线程2的run()方法中,我们将其写为前面的场景

if(a.getL()){
    a.show();
} 
结果将被排序。 执行顺序为

1,2,3,4,5,6和输出将是

3

3

真正

这是问题。 对于相同的输出,执行顺序可以如下吗?

2,1,3,4,5,6

答案是“是”。 因此,必须在l之前分配j和k。 但是没有必要在k之前分配j。

因此,j和k可能是非易失性的。 仅l需要是易失的。

5>

什么是挥发物

回答:

1>将易失性变量写入内存(为其分配了一些值)时,请确保在此易失性变量的写操作之前,必须在同一线程中的代码中进行所有先前的赋值/内存写操作。

2>每当读取volatile变量时,都必须从内存中读取它。 这就是为什么它可能会看到该值的原因,该值是在此读取之前编写的。

3>每当写入它们时,它们就会写入内存,而不是线程的本地缓存。

关于volatile还有其他一些事情。 我将在下一篇文章中提出。

From: https://bytes.com/topic/java/insights/951590-java-thread-essentials

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值