violate关键字

Violate关键字

引用:https://www.cnblogs.com/dolphin0520/p/3920373.html
这篇博客讲的比较好。
个人理解:violate能够保证可见性和有序性,

可见性:

例子如下:

//线程1
boolean stop = false;
while(!stop){
    doSomething();
}
 
//线程2
stop = true;

当线程1,stop为共享变量,放在主存中,线程1读取一份数据放到工作内存中,进行while循环,而线程二,运行时也读取了stop变量到工作内存中,但是单线程二赋值stop为true时,没发立即将stop的值写入到主存中,只会写入到工作内存。
当将stop用violate关键字修饰时,发生修改后,会产生3中效果。
1.将stop值,立即写入主存。
2.将线程1中工作内存的stop值缓存行,置为无效。
3.由于线程1读取stop值时,工作内存无效,会重新去主存读取。

顺序性:

例子如下:

//线程1:
context = loadContext();   //语句1
inited = true;             //语句2
 
//线程2:
while(!inited ){
  sleep()
}
doSomethingwithconfig(context);

inited被violate修饰时,保证context已经初始化完毕,效果是:
1.当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行。
2. 在进行指令优化时,不能将在对volatile变量访问前的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。

violate没发保证原子性

public class Test {
    public volatile int inc = 0;
     
    public void increase() {
        inc++;
    }
     
    public static void main(String[] args) {
        final Test test = new Test();
        for(int i=0;i<10;i++){
            new Thread(){
                public void run() {
                    for(int j=0;j<1000;j++)
                        test.increase();
                };
            }.start();
        }
         
        while(Thread.activeCount()>1)  //保证前面的线程都执行完
            Thread.yield();
        System.out.println(test.inc);
    }
}

violate不能保证原子性,violate的使用场景是,

  1. 状态标记量
volatile boolean flag = false;
 
while(!flag){
    doSomething();
}
 
public void setFlag() {
    flag = true;
}
volatile boolean inited = false;
//线程1:
context = loadContext();  
inited = true;            
 
//线程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);
  1. 双重循环校验
class Singleton{
    private volatile static Singleton instance = null;
     
    private Singleton() {
         
    }
     
    public static Singleton getInstance() {
        if(instance==null) {
            synchronized (Singleton.class) {
                if(instance==null)
                    instance = new Singleton();
            }
        }
        return instance;
    }
}

双重校验理解:
1.第一次判断(instance==null)是为了减少线程访问时都需要加锁后在判断,影响效率。所以在加锁前做了判断,如果获取锁后,继续判断是否初始化成功,如果没成功在初始化。
2.此时初始化,是分成3个部分,
a.开辟内存对象。
b.初始化对象。
c.将内存对象引用赋值给变量。
b和c可能会发生重排序,这时如果有线程走到第一次判断,则会拿到一个未初始化的对象,运行会发生错误。

在Java中,编写比较方法时需要遵守“通用契约”,这是为了保证排序算法的正确性。然而,有时候会出现“java时间排序comparison method violate lts general contract”的错误。这是因为在比较方法中违反了通用契约的规定。 通用契约中指定了三个规则: 1. 反对称性(Antisymmetry):如果两个对象进行比较,其中一个大于另一个,那么另一个一定小于前者。如果两个对象相等,它们之间的比较结果应为0。 2. 传递性(Transitivity):如果对象A大于对象B,B大于对象C,那么A一定大于C。同样地,如果A等于B,B等于C,那么A一定等于C。 3. 一致性(Consistency):在比较过程中,如果两个对象发生变化,它们之间的比较结果应该随之改变。 当我们在比较Java中的时间时,经常使用的是Date类。要满足通用契约,我们需要按照时间顺序进行比较。然而,有时候我们在编写比较方法时可能没有正确地实现这些规则,导致了“java时间排序comparison method violate lts general contract”错误。 要解决这个问题,我们需要回顾一下比较方法的实现,并确保满足通用契约的规定。首先,我们应该确保在比较中使用的属性是可比较的,并且没有为null的情况。然后,我们应该检查比较方法是否正确地比较了两个对象的时间顺序,并根据比较结果返回正确的值。 此外,还有一种可能导致此错误的情况是,排序算法中使用了不支持通用契约的比较方法。在这种情况下,我们应该更改排序算法或者使用其他可靠的比较方法。 总之,解决“java时间排序comparison method violate lts general contract”错误,首先我们需要检查并修复比较方法中的错误。确保按照通用契约的规定进行比较,并且没有使用不支持通用契约的比较方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值