JAVA-volatile 코스메틱 참조 유형 변수는 속성의 가시성을 보장합니까?

먼저 결론: 안 돼!


volatile는 대상을 수식합니다. 만약 대상의 속성 (구성원 변수) 이 바뀌면volatile는 다른 라인이 이 변화를 관찰할 수 있도록 보장할 수 없습니다.
처음에 나는 일부 문장에서volatile가 인용 유형의 변수를 수식하면 '인용'의 주소 변화 (즉 변수가 다른 대상을 가리키는 것) 는 다른 라인에 대해 볼 수 있지만 인용 대상의 속성 변화는 다른 라인에 대해 볼 수 없다는 것을 보았다.
실사구시의 정신에 따라 나는 몇 가지 예를 썼다. 몇 가지 시도를 통해 일반 대상의 속성의 변화를 발견했고volatile는 그 변화가 볼 수 있다는 것을 보장할 수 있다.
하지만!!!
대량의 테스트를 한 후에 나는 다른 라인이 매번 대상 속성의 변화를 관찰할 수 있는 것이 아니라는 것을 발견했다. 특히 대상의 속성이 여러 번 변화할 때!따라서 결론은volatile는 인용 유형 대상의 속성의 가시성을 확실히 보장할 수 없다는 것이다.
또 하지만!만약에 대상 속성의 라인을 수정하고 슬립을 한 번 하면 1초라도 볼 수 있다. 정말 이상하다. 나중에 시간이 있으면 JVM이나 운영체제의 메모리 관리를 연구해 보면 알 수 없을 것이다.(sleep일 수도 있고, 테스트 횟수가 많지 않을 수도 있다)
다음은 테스트의 예입니다. 만약에 여러 번(10번 정도)을 실행하면volatile 수식의 인용 유형 변수를 볼 수 있습니다. 루틴 t2는 이 인용 유형 대상의 속성을 수정하고 있습니다. 만약에 수정된 루틴 t2(생산자)가sleep가 없다면 t1은 매번 어떤 변화를 관찰할 수 없습니다.
/**
 * @author liweizhi
 * @date 2020/3/4 18:18
 */
public class VolatileObject {
    volatile static Pet pet = new Pet("dahuang", 1);

    public static void main(String[] args) {
        //  ageChange nameChange, t1 ( t2 sleep)
        nameChange();
//        ageChange();
    }

    private static void ageChange() {
        new Thread(() -> {
            System.out.println("t1 start "/* + Instant.now()*/);
            while (true) {
                if (pet.getAge() == 5) {
                    break;
                }
            }
            System.out.println("t1 end "/* + Instant.now()*/);
        }, "t1").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            Pet myPet = pet;
            for (int i = 1; i <= 100; i++) {
                int age = myPet.getAge();
                myPet.setAge(++age);
                /*try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }*/
            }
            System.out.println("t2 end "/* + Instant.now()*/);
        }, "t2").start();
    }

    private static void nameChange() {
        new Thread(() -> {
            System.out.println("t1 start "/* + Instant.now()*/);
            while (true) {
                if ("xiaobai8".equals(pet.getName())) {
                    break;
                }
            }
            System.out.println("t1 end "/* + Instant.now()*/);
        }, "t1").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            Pet myPet = pet;
            for (int i = 1; i <= 10; i++) {
                myPet.setName("xiaobai" + i);
                /*try {
                    TimeUnit.NANOSECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }*/
            }
            System.out.println("t2 end "/* + Instant.now()*/);
        }, "t2").start();
    }

    static class Pet {
        String name;

        int age;

        public Pet(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }
}

좋은 웹페이지 즐겨찾기