멀티 스레딩이란? 멀티 스레딩을 달성하는 방법은 무엇입니까?


전송 위치 : https://blog.csdn.net/csdnnews/article/details/82321777

프로세스 란 무엇입니까?

컴퓨터에서 개별적으로 실행되는 많은 프로그램이 있으며 각 프로그램에는 독립적 인 프로세스가 있으며 프로세스는 서로 독립적으로 존재합니다. 예를 들어, 아래 그림에서 QQ, Kugou 플레이어, 컴퓨터 가정부 등.여기에 사진 설명 삽입

스레드 란 무엇입니까?

프로세스는 작업을 수행하기 위해 스레드에 의존해야합니다. 즉, 프로세스에서 실행의 가장 작은 단위는 스레드이며 프로세스에는 적어도 하나의 스레드가 있습니다.

그렇다면 멀티 스레딩이란 무엇입니까? 멀티 스레딩에 관해서는 직렬과 병렬이라는 두 가지 개념에 대해 이야기 할 수 있는데, 이것을 알아 내야 만 멀티 스레딩을 더 잘 이해할 수 있습니다.

소위 직렬은 실제로 여러 작업을 수행하기 위해 단일 스레드에 상대적입니다. 다운로드 파일을 예로 들어 보겠습니다. 여러 파일을 다운로드 할 때 일련의 특정 순서로 다운로드됩니다. 예, 즉, A의 다운로드가 B의 다운로드를 시작하기를 기다려야하며 시간이 겹치는 것은 불가능합니다.
여기에 사진 설명 삽입
병렬 : 여러 파일을 다운로드하고 여러 스레드를 열고 동시에 여러 파일을 다운로드합니다. 이것은 엄밀한 의미에서 동시에 발생합니다. 병렬은 시간이 겹칩니다.
여기에 사진 설명 삽입
이 두 가지 개념을 이해 한 후 멀티 스레딩이 무엇인지 살펴 보겠습니다. 예를 들어, Tencent Manager를 열고 Tencent Manager 자체는 프로그램입니다. 즉, 프로세스라고 말하면 많은 기능이 있으며 아래 그림을 볼 수 있으며 바이러스를 검사하고 죽일 수 있으며 쓰레기를 정리하고 컴퓨터 가속 및 기타 많은 기능.

단일 스레드에 따르면 쓰레기를 정리하든 바이러스를 검사하든 다음 작업을 수행하기 전에 하나의 작업을 수행해야하며 실행 순서가 있습니다.

멀티 스레드라면 쓰레기를 정리할 때 바이러스 탐지, 컴퓨터 가속 등의 다른 작업을 실제로 수행 할 수 있습니다. 이는 엄격한 의미에서 동시에 발생하며 실행 순서가 없습니다.
여기에 사진 설명 삽입
위는 프로세스가 실행 중일 때 여러 스레드가 생성된다는 것입니다.

이 문제를 이해 한 후에는 멀티 스레딩 스레드 안전성을 사용할 때 고려해야 할 또 다른 문제를 이해해야합니다.

오늘 우리는 스레드의 안전성을 보장하는 방법에 대해 이야기하지 않을 것입니다. 스레드 안전성이란 무엇입니까? 전에 인터뷰에서 물어 봤기 때문에 솔직히이 문제를 잘 이해하지 못했어요 스레드 안전성을 확보하는 방법 만 배운 것 같지만 소위 안전성이 무엇인지 모릅니다!

스레드 안전성이란 무엇입니까?

여러 스레드가 메서드에 액세스 할 때 호출 방법이나 이러한 스레드가 번갈아 실행되는 방식에 관계없이 기본 프로그램에서 동기화를 수행 할 필요가 없습니다.이 클래스의 결과 동작은 올바른 동작을 구상 한 것입니다. 이 클래스는 스레드로부터 안전하다고 말합니다.
스레드 안전 문제이기 때문에 다중 스레드 액세스의 경우 모든 숨겨진 위험이 생성된다는 것은 의심 할 여지가 없습니다. 즉, 다중 스레드에 액세스 할 때 예상되는 동작에 따라 프로그램이 동작 할 수 있는지 확인해야합니다. , 다음 코드를 살펴 보겠습니다.

Integer count = 0;
public void getCount() {
       count ++;
       System.out.println(count);
 }

아주 간단한 코드로,이 메서드의 액세스 횟수를 세어 보겠습니다. 여러 스레드가 동시에 액세스 할 때 문제가 없습니다 .3 개의 스레드를 열고 각 스레드를 10 번 반복하여 다음과 같은 결과를 얻었습니다.
여기에 사진 설명 삽입

여기에 두 개의 26이 있음을 알 수 있습니다.이 상황은 분명히이 메서드가 스레드로부터 안전하지 않음을 보여줍니다.이 문제에는 여러 가지 이유가 있습니다.

가장 일반적인 것은 스레드 A가 메소드에 들어간 후 count 값을 가져 와서이 값을 읽고, count 값이 변경되지 않았을 때 스레드 B도 결과적으로 들어오는 것입니다. 스레드 A로 이어지고 스레드 B가받은 카운트 값은 동일합니다.

그래서 이것으로부터 우리는 이것이 실제로 스레드로부터 안전한 클래스가 아니라는 것을 이해할 수 있습니다. 왜냐하면 그들은 모두이 공유 변수를 조작해야하기 때문입니다. 사실 스레드 안전성에 대한 명확한 정의를 내리는 것은 매우 복잡합니다. 우리 프로그램에 기반한 스레드 안전성이 무엇인지 요약 해 보겠습니다.

여러 스레드가 메서드에 액세스 할 때 호출 방법이나 이러한 스레드가 번갈아 실행되는 방식에 관계없이 기본 프로그램에서 동기화를 수행 할 필요가 없습니다.이 클래스의 결과 동작은 올바른 동작을 구상 한 것입니다. 이 클래스는 스레드로부터 안전하다고 말합니다.

스레드 안전성이 무엇인지 파악한 후 Java에서 스레드 안전성을 보장하는 가장 일반적인 두 가지 방법을 살펴 보겠습니다. 먼저 코드를 살펴 보겠습니다.

public void threadMethod(int j) {
int i = 1;

j = j + i;

}

이 코드가 스레드로부터 안전하다고 생각하십니까?

절대적으로 스레드로부터 안전하다는 것은 의심의 여지가 없습니다. 스레드로부터 안전한 이유를 분석해 보겠습니다.

이 코드에는 상태가 없음을 알 수 있습니다. 즉, 코드에 범위가 포함되어 있지 않으며 참조를 위해 다른 클래스의 도메인을 참조하지 않습니다. 실행 범위와 실행 결과 만 존재합니다. 이 스레드의 로컬 변수에 있으며 실행중인 스레드에서만 액세스 할 수 있습니다. 현재 스레드의 액세스는 동일한 메서드에 액세스하는 다른 스레드에 영향을주지 않습니다.

두 스레드가 동시에이 메서드에 액세스합니다. 공유 데이터가 없기 때문에 해당 동작은 다른 스레드의 작업과 결과에 영향을주지 않으므로 상태 비 저장 개체도 스레드로부터 안전합니다.

상태를 추가하는 것은 어떻습니까?

이 코드에 상태를 추가하고이 메서드의 적중 수를 기록하는 카운트를 추가하고 각 요청에 대해 count + 1을 추가하면이 스레드가 현재 안전한가요?

public class ThreadDemo {

int count = 0; // 메서드의 적중 횟수 기록

public void threadMethod (int j) {

   count++ ;

   int i = 1;

   j = j + i;

}
}

당연히 더 이상은 아닙니다. 단일 쓰레드 연산에는 문제가 없지만 여러 쓰레드가 동시에이 메소드에 접근하면 문제가 발생하므로 먼저 count + 1 연산을 분석해 보겠습니다.

이 방법을 입력 한 후에는 먼저 count 값을 읽은 다음 count 값을 수정하고 마지막으로이 값을 count에 할당해야합니다. 총 세 단계가 포함됩니다. "read"one> "modify"one> "assign" ,이 과정은 단계별이므로 먼저 다음 그림을보고 문제를 볼 수 있는지 살펴 보겠습니다.
여기에 사진 설명 삽입
count 값이 올바른 결과가 아님을 알 수 있습니다. thread A가 count 값을 읽으면, 하지만 수정하기 전에 쓰레드 B가 이미 들어 왔고 쓰레드 B는 count의 값을 1로 읽습니다.이 때문에 우리의 count 값이 이미 벗어 났으므로 그러한 프로그램은 우리 코드에 배치됩니다. 숨겨진 위험이 많이 있습니다.

스레드 안전을 보장하는 방법은 무엇입니까?

스레드 안전성 문제가 있기 때문에이 문제를 해결하는 방법, 해결 방법을 찾아야합니다. 몇 가지 일반적인 방법에 대해 이야기합시다

동기화 됨

동기화 된 키워드는 스레드 동기화를 제어하고 다중 스레드 환경에서 스레드가 동시에 여러 스레드에 의해 실행되지 않도록하며 데이터의 무결성을 보장하는 데 사용됩니다. 사용 방법은 일반적으로 다음에 추가됩니다. 방법.

public class ThreadDemo {

int count = 0; // 메서드의 적중 횟수 기록

공개 동기화 무효 threadMethod (int j) {

   count++ ;

   int i = 1;

   j = j + i;

}
}

이러한 방식으로 스레드가 동기화되었는지 확인할 수 있습니다. 동시에 모든 사람이 일반적으로 무시하는 문제에주의를 기울여야합니다. 첫째, 동기화는 코드가 아닌 괄호 안에 객체를 잠급니다. 정적 동기화 메서드에서 잠금은 객체 자체입니다.

동기화 된 객체를 잠글 때 다른 스레드가 잠금 객체를 얻으려면 해당 스레드가 실행을 완료하고 잠금 객체를 해제 할 때까지 기다려야합니다. 그렇지 않으면 대기 상태에 있습니다.

참고 : 동기화 된 키워드를 추가하면 스레드를 더 안전하게 만들 수 있지만 사용할 때 동기화 사용 범위를 좁히는 데에도주의를 기울여야합니다. 마음대로 사용하면 프로그램의 성능에 영향을 미치고 기타 잠금을하면 잠금을 사용하지 않고 계속 잠금을 점유하게되는데 이는 리소스 낭비입니다.

자물쇠

먼저 그것과 동기화 된 것의 차이점에 대해 이야기 해 보죠 Lock은 Java 1.6에서 도입되었습니다. Lock의 도입은 잠금 작동 성을 만듭니다. 무슨 의미입니까? 즉, 수동으로 잠금을 획득하고 해제해야 할 때 획득 및 타임 아웃 획득의 동기화 기능을 중단 할 수도 있지만 사용 관점에서 볼 때 잠금은 분명히 동기화되지 않아 편리하고 빠르게 사용할 수 있습니다. 일반적으로 어떻게 사용되는지 먼저 살펴 보겠습니다.

private Lock lock = new ReentrantLock(); // ReentrantLock是Lock的子类

private void method (Thread thread) { lock.lock (); // 잠금 객체 획득 try { System.out.println ( "Thread name :"+ thread.getName () + "잠금 획득"); // 스레드 . sleep (2000); } catch (Exception e) { e.printStackTrace (); } finally { System.out.println ( "thread name :"+ thread.getName () + "released the lock"); lock.unlock (); // 잠금 객체 해제 } }










메서드에 들어가려면 먼저 잠금을 획득 한 다음 비즈니스 코드를 실행해야합니다. 여기서 차이점은 Lock으로 획득 한 객체를 스스로 해제해야한다는 점입니다. 코드의 이상을 방지하기 위해 잠금 해제 작업을 수행합니다. 어쨌든 finally의 코드가 실행되기 때문에 마지막으로 배치됩니다.

메인 메소드를 작성하고 두 개의 스레드를 열어 프로그램이 정상인지 테스트합니다.

public static void main(String[] args) {
       LockTest lockTest = new LockTest();
   // 线程1
   Thread t1 = new Thread(new Runnable() {

       @Override
       public void run() {
           // Thread.currentThread()  返回当前线程的引用
           lockTest.method(Thread.currentThread());
       }
   }, "t1");

   // 线程2
   Thread t2 = new Thread(new Runnable() {

       @Override
       public void run() {
           lockTest.method(Thread.currentThread());
       }
   }, "t2");

   t1.start();
   t2.start();

}

결과적으로
여기에 사진 설명 삽입
구현에 문제가 없음을 알 수 있습니다.

사실 Lock에서 잠금을 획득하는 방법은 여러 가지가 있습니다. 한 가지 더 말씀 드리겠습니다. 즉, tryLock ()이 Lock ()과 다릅니다. Lock이 잠금을 획득 할 때 잠금을 획득 할 수 없으면 항상 대기 중입니다. . 상태, 잠금을 획득 할 때까지 tryLock ()은 이와 같지 않습니다. TryLock에는 부울 반환 값이 있습니다. 잠금을 얻지 못하면 직접 false를 반환하고 대기를 중지합니다. Lock ()처럼 영원히 기다리지 않습니다. 자물쇠를 획득하십시오.

코드를 살펴 보겠습니다.

private void method(Thread thread){
       // lock.lock(); // 获取锁对象
       if (lock.tryLock()) {
           try {
               System.out.println("线程名:"+thread.getName() + "获得了锁");
               // Thread.sleep(2000);
           }catch(Exception e){
               e.printStackTrace();
           } finally {
               System.out.println("线程名:"+thread.getName() + "释放了锁");
               lock.unlock(); // 释放锁对象
           }
       }
   }

결과 : 우리는 지금 막 두 개의 스레드를 계속 사용하여 테스트하고 스레드 t1이 잠금을 획득 한 후 스레드 t2가 즉시 들어온 다음 잠금이 이미 사용 중임을 발견 한 다음 현재 대기하고 있지 않음을 알 수 있습니다.

여기에 사진 설명 삽입
첫 번째 스레드가 두 번째 스레드가 들어가는 것보다 잠금을 얻는 데 시간이 오래 걸리면 잠금 객체를 얻는 것도 불가능합니까?

그러면 대기중인 쓰레드가 5 초 동안 대기 할 수 있도록 제어 할 수 있으며, 5 초 후에 잠금을 획득 할 수 없으면 중지하고 대기합니다. 사실 tryLock ()은 대기하도록 설정할 수 있습니다.

private void method(Thread thread) throws InterruptedException {
       // lock.lock(); // 获取锁对象
   // 如果2秒内获取不到锁对象,那就不再等待
   if (lock.tryLock(2,TimeUnit.SECONDS)) {
       try {
           System.out.println("线程名:"+thread.getName() + "获得了锁");

           // 这里睡眠3秒
           Thread.sleep(3000);
       }catch(Exception e){
           e.printStackTrace();
       } finally {
           System.out.println("线程名:"+thread.getName() + "释放了锁");
           lock.unlock(); // 释放锁对象
       }
   }

}

결과 : 위의 코드를 살펴보면 lock 객체를 획득 할 때 2 초 동안 기다릴 수 있지만 lock 객체를 획득 한 후 스레드 t1이 작업을 실행하는 데 3 초가 걸리고 스레드 t2가이 시점을 기다리지 않음을 알 수 있습니다. 시간.
여기에 사진 설명 삽입
이 대기 시간을 5 초로 변경 한 다음 결과를 살펴 보겠습니다.

private void method(Thread thread) throws InterruptedException {
       // lock.lock(); // 获取锁对象
   // 如果5秒内获取不到锁对象,那就不再等待
   if (lock.tryLock(5,TimeUnit.SECONDS)) {
       try {
           System.out.println("线程名:"+thread.getName() + "获得了锁");
       }catch(Exception e){
           e.printStackTrace();
       } finally {
           System.out.println("线程名:"+thread.getName() + "释放了锁");
           lock.unlock(); // 释放锁对象
       }
   }

}

결과 : 이때 t2 스레드가 잠금 객체를 획득하기 위해 5 초 동안 대기하고 태스크 코드를 실행하는 것을 볼 수 있습니다.
여기에 사진 설명 삽입
위는 스레드 안전을 보장하기 위해 Lock을 사용하는 방법입니다.

추천

출처blog.csdn.net/weixin_45682070/article/details/107413334