자바 보안 인 코딩 가이드 의 가시 성과 원자 성 을 상세히 설명 합 니 다.
가 변 적 이지 않 은 대상 은 초기 화 된 후에 수정 할 수 없 는 대상 입 니 다.그러면 클래스 에 가 변 적 이지 않 은 대상 을 도입 한 것 이 아 닙 니까?가 변 적 이지 않 은 대상 에 대한 모든 수정 은 즉시 모든 스 레 드 를 볼 수 있 습 니까?
실제로 불가 변 대상 은 다 중 스 레 드 환경 에서 만 대상 이 사용 하 는 안전성 을 보장 할 수 있 을 뿐 대상 의 가시 성 을 보장 할 수 없다.
먼저 가 변성 에 대해 토론 합 시다.우 리 는 다음 의 예 를 고려 합 니 다.
public final class ImmutableObject {
private final int age;
public ImmutableObject(int age){
this.age=age;
}
}
우 리 는 Immutable Object 대상 을 정 의 했 습 니 다.class 는 final 이 고 그 안의 유일한 필드 도 final 입 니 다.그래서 이 Immutable Object 가 초기 화 되면 바 꿀 수 없습니다.그리고 get 과 set 이라는 Immutable Object 를 정의 합 니 다.
public class ObjectWithNothing {
private ImmutableObject refObject;
public ImmutableObject getImmutableObject(){
return refObject;
}
public void setImmutableObject(int age){
this.refObject=new ImmutableObject(age);
}
}
위의 예 에서 우 리 는 변 하지 않 는 대상 에 대한 refObject 인용 을 정의 한 다음 get 과 set 방법 을 정의 했다.비록 Immutable Object 라 는 클래스 자체 가 변 할 수 없 지만,우 리 는 이 대상 에 대한 refObject 인용 은 변 할 수 있 습 니 다.이것 은 우리 가 setImmutable Object 방법 을 여러 번 호출 할 수 있다 는 것 을 의미한다.
가시 성 을 다시 토론 하 다.
위의 예 에서 다 중 스 레 드 환경 에서 setImmutable Object 는 매번 getImmutable Object 를 새로운 값 으로 되 돌려 줍 니까?
답 은 부정 적 이다.
원본 코드 를 컴 파일 한 후에 컴 파일 러 에서 생 성 된 명령 의 순 서 는 원본 코드 의 순서 와 완전히 일치 하지 않 습 니 다.프로 세 서 는 명령 을 어 지 럽 히 거나 병행 하 는 방식 으로 실행 할 수 있 습 니 다.또한 프로세서 와 로 컬 캐 시 는 결 과 를 로 컬 캐 시 에 저장 하면 다른 스 레 드 는 결 과 를 볼 수 없습니다.그 밖 에 캐 시 를 주 메모리 에 제출 하 는 순서 도 달라 질 수 있 습 니 다.
어떻게 해결 하지?
가장 간단 한 해결 방법 은 volatile 키 워드 를 추가 하 는 것 입 니 다.volatile 키 워드 는 자바 메모리 모델 의 happens-before 규칙 을 사용 하여 volatile 의 변 수 를 모든 스 레 드 에 보 여 줍 니 다.
public class ObjectWithVolatile {
private volatile ImmutableObject refObject;
public ImmutableObject getImmutableObject(){
return refObject;
}
public void setImmutableObject(int age){
this.refObject=new ImmutableObject(age);
}
}
또한 자물쇠 메커니즘 을 사용 하면 같은 효 과 를 얻 을 수 있다.
public class ObjectWithSync {
private ImmutableObject refObject;
public synchronized ImmutableObject getImmutableObject(){
return refObject;
}
public synchronized void setImmutableObject(int age){
this.refObject=new ImmutableObject(age);
}
}
마지막 으로 우 리 는 원자 류 를 사용 하여 같은 효 과 를 얻 을 수 있다.
public class ObjectWithAtomic {
private final AtomicReference<ImmutableObject> refObject= new AtomicReference<>();
public ImmutableObject getImmutableObject(){
return refObject.get();
}
public void setImmutableObject(int age){
refObject.set(new ImmutableObject(age));
}
}
공유 변수의 복합 작업 의 원자 성 을 확보 하 다.공유 대상 이 라면 다 중 스 레 드 환경 에서 의 원자 성 을 고려 해 야 한다.공유 변수 에 대한 복합 작업 이 라면:+,--*=,/=,%=,+=,-=,<=,>=,>>=,>=,^=등 은 하나의 문장 으로 보이 지만 실제로는 여러 문장의 집합 이다.
우 리 는 다 중 스 레 드 아래 의 안전성 을 고려 해 야 한다.
아래 의 예 를 고려 하 다.
public class CompoundOper1 {
private int i=0;
public int increase(){
i++;
return i;
}
}
예 에서 우 리 는 int i 에 대해 누적 작업 을 한다.그러나++는 실제로 세 가지 조작 으로 이 루어 져 있다.1.메모리 에서 i 값 을 읽 고 CPU 레지스터 에 기록 합 니 다.
2.CPU 레지스터 에서 i 값+1
3.메모리 에 있 는 i 에 값 을 다시 씁 니 다.
단일 스 레 드 환경 에 서 는 문제 가 없 지만 다 중 스 레 드 환경 에 서 는 원자 조작 이 아니 기 때문에 문제 가 발생 할 수 있 습 니 다.
해결 방법 은 여러 가지 가 있 는데,첫 번 째 는 synchronized 키 워드 를 사용 하 는 것 이다.
public synchronized int increaseSync(){
i++;
return i;
}
두 번 째 는 lock 을 사용 하 는 것 입 니 다.
private final ReentrantLock reentrantLock=new ReentrantLock();
public int increaseWithLock(){
try{
reentrantLock.lock();
i++;
return i;
}finally {
reentrantLock.unlock();
}
}
세 번 째 는 Atomic 원자 류 를 사용 하 는 것 이다.
private AtomicInteger atomicInteger=new AtomicInteger(0);
public int increaseWithAtomic(){
return atomicInteger.incrementAndGet();
}
여러 Atomic 원자 류 작업 의 원자 성 을 확보 합 니 다.만약 한 방법 이 여러 개의 원자 류 의 조작 을 사용 했다 면,비록 하나의 원자 조작 은 원자 성 이지 만,조합 해 보면 반드시 그렇지 는 않다.
우 리 는 예 를 하나 보 자.
public class CompoundAtomic {
private AtomicInteger atomicInteger1=new AtomicInteger(0);
private AtomicInteger atomicInteger2=new AtomicInteger(0);
public void update(){
atomicInteger1.set(20);
atomicInteger2.set(10);
}
public int get() {
return atomicInteger1.get()+atomicInteger2.get();
}
}
위의 예 에서 우 리 는 두 개의 AtomicInteger 를 정 의 했 고 각각 update 와 get 작업 에서 두 개의 AtomicInteger 를 조작 했다.AtomicInteger 는 원자 적 이지 만 두 개의 서로 다른 AtomicInteger 를 합치 면 그렇지 않다.다 중 스 레 드 작업 과정 에서 문제 가 생 길 수 있 습 니 다.
마찬가지 로 우 리 는 동기 화 메커니즘 이나 자 물 쇠 를 사용 하여 데이터 의 일치 성 을 확보 할 수 있다.
보증 방법 호출 체인 의 원자 성
만약 우리 가 대상 의 인 스 턴 스 를 만 들 려 고 한다 면,이 대상 의 인 스 턴 스 는 체인 호출 을 통 해 만 든 것 입 니 다.그러면 우 리 는 체인 호출 의 원자 성 을 확보 해 야 한다.
다음 의 예 를 고려 해 보 자.
public class ChainedMethod {
private int age=0;
private String name="";
private String adress="";
public ChainedMethod setAdress(String adress) {
this.adress = adress;
return this;
}
public ChainedMethod setAge(int age) {
this.age = age;
return this;
}
public ChainedMethod setName(String name) {
this.name = name;
return this;
}
}
아주 간단 한 대상 입 니 다.우 리 는 세 개의 속성 을 정 의 했 습 니 다.매번 set 는 this 에 대한 인용 을 되 돌려 줍 니 다.우 리 는 다 중 스 레 드 환경 에서 어떻게 호출 하 는 지 보 자.
ChainedMethod chainedMethod= new ChainedMethod();
Thread t1 = new Thread(() -> chainedMethod.setAge(1).setAdress("www.flydean.com1").setName("name1"));
t1.start();
Thread t2 = new Thread(() -> chainedMethod.setAge(2).setAdress("www.flydean.com2").setName("name2"));
t2.start();
다 중 스 레 드 환경 에서 위의 set 방법 은 혼 란 스 러 울 수 있 기 때문이다.어떻게 해결 하지?우 리 는 먼저 로 컬 복사 본 을 만 들 수 있 습 니 다.이 복사 본 은 로 컬 에서 방문 한 것 이기 때문에 스 레 드 가 안전 합 니 다.마지막 으로 복사 본 을 새로 만 든 인 스 턴 스 대상 에 게 복사 할 수 있 습 니 다.
주요 코드 는 다음 모양 입 니 다:
public class ChainedMethodWithBuilder {
private int age=0;
private String name="";
private String adress="";
public ChainedMethodWithBuilder(Builder builder){
this.adress=builder.adress;
this.age=builder.age;
this.name=builder.name;
}
public static class Builder{
private int age=0;
private String name="";
private String adress="";
public static Builder newInstance(){
return new Builder();
}
private Builder() {}
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setAge(int age) {
this.age = age;
return this;
}
public Builder setAdress(String adress) {
this.adress = adress;
return this;
}
public ChainedMethodWithBuilder build(){
return new ChainedMethodWithBuilder(this);
}
}
우 리 는 어떻게 호출 하 는 지 보 자.
final ChainedMethodWithBuilder[] builder = new ChainedMethodWithBuilder[1];
Thread t1 = new Thread(() -> {
builder[0] =ChainedMethodWithBuilder.Builder.newInstance()
.setAge(1).setAdress("www.flydean.com1").setName("name1")
.build();});
t1.start();
Thread t2 = new Thread(() ->{
builder[0] =ChainedMethodWithBuilder.Builder.newInstance()
.setAge(1).setAdress("www.flydean.com1").setName("name1")
.build();});
t2.start();
lambda 표현 식 에서 사용 하 는 변 수 는 final 또는 final 과 같은 효 과 를 가 져 야 하기 때문에 final 배열 을 구축 해 야 합 니 다.읽 기와 쓰기 64bits 의 값
자바 에 서 는 64bits 의 log 와 double 이 두 개의 32bits 로 취급 된다.
그래서 하나의 64 bits 작업 은 두 개의 32 bits 작업 으로 나 뉘 었 다.원자 문 제 를 일 으 켰 다.
다음 코드 를 고려 하 십시오:
public class LongUsage {
private long i =0;
public void setLong(long i){
this.i=i;
}
public void printLong(){
System.out.println("i="+i);
}
}
롱 의 읽 기와 쓰 기 는 두 부분 으로 나 뉘 어 진행 되 기 때문에 다 중 스 레 드 환경 에서 setLong 과 printLong 을 여러 번 호출 하 는 방법 이 있 으 면 문제 가 생 길 수 있 습 니 다.해결 방법 은 간단 합 니 다.log 또는 double 변 수 를 volatile 로 정의 하면 됩 니 다.
private volatile long i = 0;
이상 은 자바 보안 코드 매 뉴 얼 의 가시 성과 원자 성에 대한 상세 한 내용 입 니 다.자바 보안 코드 매 뉴 얼 의 가시 성과 원자 성에 관 한 자 료 는 다른 관련 글 을 주목 하 시기 바 랍 니 다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Is Eclipse IDE dying?In 2014 the Eclipse IDE is the leading development environment for Java with a market share of approximately 65%. but ac...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.