자바 노트 - 스 레 드 동기 화 (7 가지 동기 화 방식)
26939 단어 자바
--친구 가 이 글 을 전재 하고 싶 으 면 전재 주 소 를 밝 혀 주세요. "http://www.cnblogs.com/XHJT/p/3897440.html" 감사합니다.
동기 화 를 왜 사용 합 니까? 자바 는 다 중 스 레 드 병행 통 제 를 허용 합 니 다. 여러 스 레 드 가 공유 가능 한 자원 변 수 를 동시에 조작 할 때 (예 를 들 어 데이터 의 추가 삭제 검사), 데이터 가 정확 하지 않 고 서로 충돌 할 수 있 으 므 로 이 스 레 드 가 조작 되 기 전에 다른 스 레 드 에 의 해 호출 되 지 않도록 동기 화 자 물 쇠 를 추가 합 니 다. 이로써 이 변수의 유일 성과 정확성 을 확보 했다.
1. 동기 화 방법 synchronized 키 워드 를 수식 하 는 방법 이 있 습 니 다. 자바 의 모든 대상 에 내 장 된 자물쇠 가 있 기 때문에 이 키워드 로 수식 할 때, 내 장 된 자 물 쇠 는 모든 방법 을 보호 할 수 있다.이 방법 을 호출 하기 전에 내 장 된 자 물 쇠 를 가 져 와 야 합 니 다. 그렇지 않 으 면 차단 상태 에 있 습 니 다.
코드: public synchronized void save(){}
주: synchronized 키워드 도 정적 방법 을 수식 할 수 있 습 니 다. 이 정적 방법 을 사용 하면 전체 클래스 가 잠 겨 있 습 니 다.
2. 동기 코드 블록 synchronized 키 워드 를 수식 하 는 구문 블록 입 니 다. 이 키워드 에 의 해 수 식 된 문장 블록 은 자동 으로 내 장 된 자 물 쇠 를 추가 하여 동기 화 를 실현 한다
코드: synchronized(object){ }
주: 동기 화 는 비용 이 많이 드 는 작업 이기 때문에 동기 화 된 내용 을 최대한 줄 여야 합 니 다. 일반적으로 전체 방법 을 동기 화 할 필요 가 없 으 며, synchronized 코드 블록 을 사용 하여 키 코드 를 동기 화하 면 됩 니 다. 코드 인 스 턴 스:
package com.xhj.thread;
/**
*
*
* @author XIEHEJUN
*
*/
public class SynchronizedThread {
class Bank {
private int account = 100;
public int getAccount() {
return account;
}
/**
*
*
* @param money
*/
public synchronized void save(int money) {
account += money;
}
/**
*
*
* @param money
*/
public void save1(int money) {
synchronized (this) {
account += money;
}
}
}
class NewThread implements Runnable {
private Bank bank;
public NewThread(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
// bank.save1(10);
bank.save(10);
System.out.println(i + " :" + bank.getAccount());
}
}
}
/**
* ,
*/
public void useThread() {
Bank bank = new Bank();
NewThread new_thread = new NewThread(bank);
System.out.println(" 1");
Thread thread1 = new Thread(new_thread);
thread1.start();
System.out.println(" 2");
Thread thread2 = new Thread(new_thread);
thread2.start();
}
public static void main(String[] args) {
SynchronizedThread st = new SynchronizedThread();
st.useThread();
}
}
3. 특수 도 메 인 변수 (volatile) 를 사용 하여 스 레 드 동기 화
a. volatile 키 워드 는 도 메 인 변수의 방문 에 잠 금 면제 체 제 를 제공 합 니 다. b. volatile 수식 도 메 인 을 사용 하면 가상 컴퓨터 에 이 도 메 인 이 다른 스 레 드 에 의 해 업 데 이 트 될 수 있 음 을 알려 주 는 것 과 같 습 니 다. c. 따라서 이 도 메 인 을 사용 할 때마다 레지스터 의 값 을 사용 하 는 것 이 아니 라 다시 계산 해 야 합 니 다. d. volatile 은 원자 조작 을 제공 하지 않 고 final 형식의 변 수 를 수식 할 수 없습니다. 예 를 들 면: 위의 예 에서 account 앞 에 volatile 수식 을 추가 하면 스 레 드 동기 화 를 실현 할 수 있 습 니 다. 코드 인 스 턴 스:
// ,
class Bank {
// volatile
private volatile int account = 100;
public int getAccount() {
return account;
}
// synchronized
public void save(int money) {
account += money;
}
}
주: 다 중 스 레 드 의 비 동기 화 문 제 는 주로 도 메 인 에 대한 읽 기와 쓰기 에 나타 납 니 다. 도 메 인 자체 가 이 문 제 를 피 할 수 있 으 면 이 도 메 인 을 조작 하 는 방법 을 수정 할 필요 가 없습니다. final 도 메 인 을 사용 하면 잠 금 보호 도 메 인과 volatil e 도 메 인 이 동기 화 되 지 않 는 문 제 를 피 할 수 있 습 니 다. 4. 재 접속 잠 금 으로 스 레 드 동기 화
자바 SE 5.0 에 자바 util. concurrent 패 키 지 를 추가 하여 동기 화 를 지원 합 니 다. ReentrantLock 류 는 다시 들 어 갈 수 있 고 서로 배척 할 수 있 으 며 Lock 인터페이스의 자 물 쇠 를 실현 합 니 다. 그것 은 synchronized 방법 과 빠 른 것 을 사용 하 는 것 과 같은 기본 적 인 행위 와 의 미 를 가지 고 그 능력 을 확장 시 켰 다.
ReenreantLock 류 의 일반적인 방법 은 다음 과 같 습 니 다.
ReentrantLock (): ReentrantLock 인 스 턴 스 를 만 듭 니 다. lock (): 자물쇠 획득 잠 금 해제 (): 잠 금 해제 주: ReentrantLock () 은 공정 한 자 물 쇠 를 만 들 수 있 는 구조 방법 도 있 지만 프로그램 운행 효율 을 대폭 낮 출 수 있 기 때문에 사용 하 는 것 을 추천 하지 않 습 니 다. 예 를 들 면: 위의 예 를 바탕 으로 고 친 코드 는 다음 과 같다. 코드 인 스 턴 스:
// ,
class Bank {
private int account = 100;
//
private Lock lock = new ReentrantLock();
public int getAccount() {
return account;
}
// synchronized
public void save(int money) {
lock.lock();
try{
account += money;
}finally{
lock.unlock();
}
}
}
주: Lock 대상 과 synchronized 키워드 에 대한 선택: a. 둘 다 사용 하지 않 고 자바 util. concurrent 패키지 가 제공 하 는 메커니즘 을 사용 하 는 것 이 좋 습 니 다. 사용자 가 자물쇠 와 관련 된 모든 코드 를 처리 하 는 데 도움 을 줄 수 있 습 니 다. b. synchronized 키워드 가 사용자 의 수 요 를 만족 시 킬 수 있다 면 synchronized 를 사용 하 십시오. 코드 를 간소화 할 수 있 기 때 문 입 니 다. c. 더 높 은 기능 이 필요 하 다 면 ReentrantLock 류 를 사용 합 니 다. 이때 자 물 쇠 를 제때에 풀 어야 합 니 다. 그렇지 않 으 면 자물쇠 가 생 길 수 있 습 니 다. 보통 finally 코드 에서 자 물 쇠 를 풀 어야 합 니 다. 5. 부분 변 수 를 사용 하여 스 레 드 동기 화 실현 ThreadLocal 관리 변 수 를 사용 하면 이 변 수 를 사용 하 는 모든 스 레 드 는 이 변수의 사본 을 얻 습 니 다. 던 전 간 에 서로 독립 되 어 있 습 니 다. 그러면 모든 스 레 드 는 자신의 변수 던 전 을 마음대로 수정 할 수 있 고 다른 스 레 드 에 영향 을 주지 않 습 니 다.
ThreadLocal 클래스 의 일반적인 방법
ThreadLocal (): 스 레 드 로 컬 변 수 를 만 듭 니 다. get (): 이 스 레 드 부분 변수의 현재 스 레 드 던 전의 값 을 되 돌려 줍 니 다. initialValue (): 이 스 레 드 부분 변수의 현재 스 레 드 의 "초기 값" 을 되 돌려 줍 니 다. set (T value): 이 스 레 드 부분 변수의 현재 스 레 드 던 전의 값 을 value 로 설정 합 니 다.
예 를 들 면: 위의 예 를 바탕 으로 수 정 된 코드 는 다음 과 같다. 코드 인 스 턴 스:
// Bank ,
public class Bank{
// ThreadLocal account
private static ThreadLocal<Integer> account = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue(){
return 100;
}
};
public void save(int money){
account.set(account.get()+money);
}
public int getAccount(){
return account.get();
}
}
주: ThreadLocal 과 동기 화 메커니즘 a. ThreadLocal 과 동기 화 체 제 는 다 중 스 레 드 에서 같은 변수의 접근 충돌 문 제 를 해결 하기 위해 서 입 니 다. b. 전 자 는 '공간 으로 시간 을 바꾼다' 는 방법 을 사용 하고 후 자 는 '시간 으로 공간 을 바꾼다' 는 방식 을 사용한다.
6. 차단 대기 열 을 사용 하여 스 레 드 동기 화
앞의 5 가지 동기 화 방식 은 모두 밑바닥 에서 실 현 된 라인 동기 화 이지 만 우 리 는 실제 개발 에서 밑바닥 구 조 를 최대한 멀리 해 야 한다. javaSE 5.0 버 전에 추 가 된 java. util. concurrent 패 키 지 를 사용 하면 개발 을 간소화 하 는 데 도움 이 될 것 입 니 다. 이 소절 은 주로 링크 드 BlockingQueue < E > 를 사용 하여 스 레 드 의 동기 화 를 실현 합 니 다. 링크 드 BlockingQueue < E > 는 연 결 된 노드 를 기반 으로 하 는 범위 가 임의의 blocking queue 입 니 다. 대열 은 먼저 나 가 는 순서 (FIFO) 입 니 다. 대열 에 대해 서 는 나중에 자세히 설명 하 겠 습 니 다 ~ 링크 드 BlockingQueue 클래스 상용 방법 LinkedBlockingQueue (): Integer. MAX VALUE 용량 의 LinkedBlockingQueue 만 들 기 put (E e): 줄 끝 에 요 소 를 추가 합 니 다. 대기 열 이 가득 차 면 차단 합 니 다. size (): 대기 열 에 있 는 요소 개 수 를 되 돌려 줍 니 다. take (): 헤더 요 소 를 제거 하고 되 돌려 줍 니 다. 대기 열 이 비어 있 으 면 차단 합 니 다. 코드 인 스 턴 스: 업 체 의 상품 생산 과 매매 상품 의 동기 화 를 실현 하 다.
1 package com.xhj.thread;
2
3 import java.util.Random;
4 import java.util.concurrent.LinkedBlockingQueue;
5
6 /**
7 * LinkedBlockingQueue
8 *
9 * @author XIEHEJUN
10 *
11 */
12 public class BlockingSynchronizedThread {
13 /**
14 *
15 */
16 private LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();
17 /**
18 *
19 */
20 private static final int size = 10;
21 /**
22 * , 0 , ; 1 ,
23 */
24 private int flag = 0;
25
26 private class LinkBlockThread implements Runnable {
27 @Override
28 public void run() {
29 int new_flag = flag++;
30 System.out.println(" " + new_flag);
31 if (new_flag == 0) {
32 for (int i = 0; i < size; i++) {
33 int b = new Random().nextInt(255);
34 System.out.println(" :" + b + " ");
35 try {
36 queue.put(b);
37 } catch (InterruptedException e) {
38 // TODO Auto-generated catch block
39 e.printStackTrace();
40 }
41 System.out.println(" :" + queue.size() + " ");
42 try {
43 Thread.sleep(100);
44 } catch (InterruptedException e) {
45 // TODO Auto-generated catch block
46 e.printStackTrace();
47 }
48 }
49 } else {
50 for (int i = 0; i < size / 2; i++) {
51 try {
52 int n = queue.take();
53 System.out.println(" " + n + " ");
54 } catch (InterruptedException e) {
55 // TODO Auto-generated catch block
56 e.printStackTrace();
57 }
58 System.out.println(" :" + queue.size() + " ");
59 try {
60 Thread.sleep(100);
61 } catch (Exception e) {
62 // TODO: handle exception
63 }
64 }
65 }
66 }
67 }
68
69 public static void main(String[] args) {
70 BlockingSynchronizedThread bst = new BlockingSynchronizedThread();
71 LinkBlockThread lbt = bst.new LinkBlockThread();
72 Thread thread1 = new Thread(lbt);
73 Thread thread2 = new Thread(lbt);
74 thread1.start();
75 thread2.start();
76
77 }
78
79 }
주: BlockingQueue < E > 는 대기 열 을 막 는 데 자주 사용 되 는 방법 을 정의 합 니 다. 특히 세 가지 요 소 를 추가 하 는 방법 은 대기 열 이 가득 찼 을 때 주의해 야 합 니 다.
add () 방법 은 이상 을 던 집 니 다.
offer () 방법 false 되 돌리 기
put () 방법 이 막 힙 니 다.
7. 원자 변 수 를 사용 하여 스 레 드 동기 화 실현
스 레 드 동기 화 를 사용 해 야 하 는 근본 적 인 원인 은 일반 변수 에 대한 조작 이 원자 가 아니 기 때문이다.
원자 조작 이란 무엇 일 까요? 원자 조작 이란 변수 값 읽 기, 변수 값 수정, 변수 값 저장 을 하나의 전체 로 보 는 것 을 말 합 니 다. - 이 몇 가지 행 위 는 동시에 완성 되 거나 완료 되 지 않 습 니 다. 자바 의 util. concurrent. atomic 패키지 에 원자 형식 변 수 를 만 드 는 도구 류 를 제공 합 니 다. 이 종 류 를 사용 하면 스 레 드 동기 화 를 간소화 할 수 있 습 니 다. 그 중에서 AtomicIntege 는r 표 는 int 값 을 원자 방식 으로 업데이트 할 수 있 습 니 다. 프로그램 에서 (예 를 들 어 원자 방식 으로 증가 하 는 계수기) 사용 할 수 있 지만 Integer 를 교체 할 수 없습니다. Number 를 확장 할 수 있 습 니 다. 기회 디지털 류 를 처리 하 는 도구 와 유 틸 리 티 도 구 를 통일 적 으로 접근 할 수 있 습 니 다. AtomicInteger 류 의 일반적인 방법: AtomicInteger (int initialValue): 주어진 초기 값 을 가 진 새로운 AtomicInteger addGet (int dalta) 을 만 듭 니 다. 주어진 값 을 현재 값 과 추가 get (): 현재 값 코드 인 스 턴 스 가 져 오기: Bank 클래스 만 바 꾸 고 나머지 코드 는 위의 첫 번 째 예 와 같 습 니 다.
1 class Bank {
2 private AtomicInteger account = new AtomicInteger(100);
3
4 public AtomicInteger getAccount() {
5 return account;
6 }
7
8 public void save(int money) {
9 account.addAndGet(money);
10 }
11 }
보충 - 원자 조작 은 주로 인용 변수 와 대부분의 원시 변수 (long 과 double 제외) 에 대한 읽 기와 쓰기, volatile 수식 을 사용 하 는 모든 변수 (long 과 double 포함) 에 대한 읽 기와 쓰기 동작 이 있 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 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에 따라 라이센스가 부여됩니다.