검토 - 멀티스레딩

멀티스레딩의 기본 개념

멀티스레딩에 대해 이야기하기 전에 먼저 CPU에 대해 알아보겠습니다. 프로세스와 스레드의 작업은 CPU와 분리될 수 없다는 사실은 누구나 알고 있습니다. 스레드가 CPU에서 실행된다고 할 수 있습니다.

CPU는 무엇입니까?

CPU의 중국어 명칭은 중앙 처리 장치로 논리적 연산에 사용되며
크게 연산 장치, 컨트롤러, 레지스터의 세 부분으로 구성됩니다.
문자 그대로 연산은 연산의 기능입니다.
컨트롤러는 다음과 같은 역할을 담당합니다 . CPU의 각 명령 발행 필수 정보인
레지스터는 작업이나 명령을 저장하는 임시 파일입니다.

프로세스란 무엇인가

여기에 이미지 설명을 삽입하세요.
프로세스는 자원 할당의 최소 단위이고, 스레드는 프로그램 실행의 최소 단위입니다. 컴퓨터는 프로그램을 실행할 때 프로그램에 해당하는 프로세스를 생성하고, 자원을 할당할 때는 프로세스 단위로 해당 할당을 수행합니다. 각 프로세스에는 해당 스레드가 있으며, 프로그램을 실행할 때 해당 일련의 스레드가 실제로 실행됩니다.
요약: 프로세스는 자원 할당의 최소 단위이고, 스레드는 프로그램 실행의 최소 단위입니다.

우리 삶의 관점에서 볼 때 프로세스는 컴퓨터를 사용하여 프로그램을 여는 것입니다. 이 프로세스 동안 CPU는 하드 디스크에서 메모리로 프로그램을 읽어 들이는데, 이것이 바로 프로세스입니다. QQ 애플리케이션을 열면, CPU는 하드 디스크에서 qq.exe를 호출한 후 진행합니다.실행, 실행된 일부 임시 파일은 메모리에 저장됩니다.
스레드는 프로그램 실행의 가장 작은 단위입니다.프로세스에는 여러 개의 서로 다른 스레드가 있을 수 있습니다.

프로세스에 스레드가 필요한 이유는 무엇입니까?

동일한 애플리케이션(프로세스)에서 더 나은 병렬 처리
여기에 이미지 설명을 삽입하세요.

멀티스레딩을 사용해야 하는 이유는 무엇입니까?

목적은 프로그램 개발의 효율성을 높이는 것입니다.
예를 들어 프로젝트에 프로그래머가 Xiao Wang 한 명 뿐이고 그는 기능 모듈, 멤버십 모듈, 결제 모듈 및 주문 모듈을 개발해야 합니다. 필요한 개발 주기는 30일이지만 이 프로젝트는 긴급하여 10일 이내에 완료해야 합니다. 이때 Xiao Zheng과 Xiao Zhu가 다른 두 모듈의 개발을 담당하고 있음을 알 수 있습니다. 이는 크게 개선될 수 있습니다. 개발 효율성. 만약 Xiao Zheng 개발 중에 버그가 발생하더라도 다른 두 사람의 진행에는 영향을 미치지 않습니다.

병렬/직렬 차이:
직렬은 단일 스레드 코드의 실행 효율성이 매우 낮고 코드가 위에서 아래로 실행된다는 것을 의미하며,
병렬은 여러 스레드가 병렬로 실행되어 효율성이 상대적으로 높다는 것을 의미합니다.

멀티스레딩을 사용하면 확실히 효율성이 향상되나요?

다중 스레드 실행에는 동시 실행이 필요합니다.


반드시 그런 것은 아닙니다. CPU 스케줄링 알고리즘은 먼저 이전 작업의 CPU 컨텍스트(즉, CPU 레지스터 및 프로그램 카운터)를 저장한 다음 새 작업의 컨텍스트를 이러한 레지스터 및 프로그램 카운터에 로드하는 것임을 이해해야 합니다 . 마지막으로 프로그램 카운터로 점프합니다. 새 위치를 가리키고 새 작업을 실행합니다.

프로덕션 환경에서 수백 또는 수천 개의 스레드가 활성화되고 서버에 8개 코어, 16개 코어 또는 32개 코어가 있는 경우 많은 스레드가 CPU에서 컨텍스트 전환을 수행합니다.

동기식과 비동기식의 차이점

동기화 개념: 코드는 위에서 아래로 실행됩니다.
비동기의 개념: 별도의 분기 실행은 서로 영향을 미치지 않습니다.

CPU 스케줄링 시간 조각

1. 싱글 코어 CPU는 한 번에 한 번만 스레드를 실행할 수 있으며, 싱글 코어 CPU에서 멀티스레딩을 활성화하면 각 스레드가 차례로 실행됩니다.
2. CPU가 단일 계산을 수행할 때마다 CPU 시간 조각이 되는데 실제로는 수십 밀리초에 불과해 마치
멀티 스레드처럼 느껴집니다.
3. 스레드의 경우 CPU 스케줄링을 기다리고 있는 경우 스레드 상태는 준비 상태이고, CPU에 의해 스케줄링된 경우 스레드 상태는 실행 상태입니다.
다른 스레드에서는 스레드가 변경되며 준비 상태입니다.

단일 코어 CPU에서 멀티스레딩이 활성화된 경우 기본 실행은 진정한 의미에서 멀티스레딩이 아닙니다.
멀티코어, 멀티스레드 성능 활용

CPU 스케줄링 알고리즘 원리

1. 선착순의 단점 실행하려는 첫 번째 스레드가 CPU를 많이 사용하는 경우 다른 스레드를 계속 실행하지 못할 수 있습니다.
2. 최단 연산 방식에서는 계산 시간이 가장 짧은 사람이 먼저 실행합니다.
3. 우선순위 스케줄링 알고리즘은 프로세스를 중요도에 따라 4개의 우선순위로 나눕니다. 우선순위 4: 프로세스
D가 화면을 담당합니다.---
우선순위 3: 프로세스 B와 프로세스 C가 사용자의 지시를 수락합니다. 우선순위 2: 프로세스 D 의 2차 우선순위
백그라운드 프로세서
1

다중 스레드 애플리케이션 시나리오

일반적으로 우리 개발에서는 멀티스레딩을 사용하는 곳이 많이 있는데 어떻게 사용하느냐에 따라 다릅니다.


멀티스레딩이 사용될 시나리오를 나열하겠습니다
: 1. 클라이언트(모바일 앱/) 개발,
2. 비동기적으로 문자 메시지 보내기/이메일 보내기
3. 시간이 많이 걸리는 코드를 멀티스레드 비동기 실행으로 전환, 인터페이스는 다음을 수행할 수 있습니다. 응답 속도 향상
4. 로그의 비동기 쓰기 로그 프레임워크 하위 계층
5. 멀티스레드 다운로드


멀티스레드를 생성하는 방법

8부작 에세이를 외워본 사람이라면 세 가지 방법이 있다는 것을 알겠지만, 사실 이 세 가지 방법 외에도 멀티스레드를 만드는 다른 방법도 있습니다.

  1. Thread 클래스를 상속받아 스레드 생성
  2. 스레드를 생성하기 위한 Runnable 인터페이스 구현
  3. 익명의 내부 클래스를 사용하여 스레드 만들기
  4. 람다 표현식을 사용하여 스레드 만들기
  5. Callable 및 Future를 사용하여 스레드 생성
  6. Executor 프레임워크와 같은 스레드 풀 사용
  7. 스레드 풀과 결합된 spring @Async 비동기 주석

Thread 클래스는 스레드를 생성합니다.

public class ThreadDemo01  extends Thread{
    
    
    /**
     * 线程执行的代码在run方法中
     */
    @Override
    public void run() {
    
    
        System.out.println(Thread.currentThread().getName()+"<run>");
        try
        {
    
    
            // 当前子线程阻塞3秒时间
            Thread.sleep(3000);
        } catch (InterruptedException e) {
    
    
            throw new RuntimeException(e);
        }
        System.out.println("阻塞完毕");
    }

    public static void main(String[] args) {
    
     // main 主线程
        System.out.println("获取当前主线程"+Thread.currentThread().getName());
        // 启动线程 调用start() 方法不是run()方法
        new ThreadDemo01().start(); // start 就绪状态 -- 等待cpu调用
        new ThreadDemo01().start();
        System.out.println("主线程执行完毕!");
    }
}

스레드를 생성하기 위한 Runnable 인터페이스 구현

public class ThreadDemo02 implements Runnable {
    
    
    public void run() {
    
    
        System.out.println(Thread.currentThread().getName() + ",我是子线程");
        // 
    }

    public static void main(String[] args) {
    
    
        new Thread(new ThreadDemo02()).start();
    }
}

익명의 내부 클래스를 사용하여 스레드 만들기

public class ThreadDemo02 {
    
    
    public static void main(String[] args) {
    
    
      // 使用匿名内部类的形式创建线程 java8的新特性
        new Thread(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                System.out.println(Thread.currentThread().getName() + ",>我是子线程<");
            }
        }).start();
    }
}

람다 표현을 사용하여 스레드 만들기

public class ThreadDemo02 {
    
    
    public static void main(String[] args) {
    
    
    // 使用lambda表示创建线程
 new Thread(()->{
    
    
            System.out.println(Thread.currentThread().getName() + ",>我是子线程<");
        }).start();
    }
}

Callable 및 Future를 사용하여 스레드 생성

public class ThreadCallable  implements Callable<Integer> {
    
    
/*  从Java 5开始,Java提供了Callable接口,该接口是Runnable接口的增强版,		Callable接口提供了一个call()方法,可以看作是线程的执行体
   主要 call()方法可以有返回值。
    call()方法可以声明抛出异常*/
    @Override
    public Integer call() throws Exception {
    
    
        // 默认代码执行非常耗时!!
        System.out.println(Thread.currentThread().getName() + ",执行计算操作");
        return 1;
    }

    public static void main(String[] args) {
    
    
        ThreadCallable threadCallable = new ThreadCallable();
        FutureTask<Integer> integerFutureTask = new FutureTask<>(threadCallable);
        new Thread(integerFutureTask).start();
        // 我们的主线程要等待我们子线程给我的返回结果
        Integer result = null;
        try {
    
    
            result = integerFutureTask.get();
        } catch (InterruptedException e) {
    
    
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
    
    
            throw new RuntimeException(e);
        }
        System.out.println("结果"+result);
    }


}

Executors 프레임워크와 같은 스레드 풀 사용

public class ThreadExectors {
    
    

    public static void main(String[] args) {
    
    
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                System.out.println("我是子线程");
            }
        });
    }
}

spring @Async 비동기 주석

@Async의 하위 레이어는 aop+custom 주석을 기반으로 구현됩니다.

@Component
@Slf4j
public class OrderManage {
    
    
    @Async
    public void asyncLog() {
    
    
        try {
    
    
            Thread.sleep(3000);
            log.info("<2>");
        } catch (Exception e) {
    
    

        }

    }
}
@RequestMapping("/addOrder")
public String addOrder() {
    
    
    log.info("<1>");
    orderManage.asyncLog();
    log.info("<3>");
    return "3";
}

멀티스레딩 스레드 안전 보안 문제

스레드 안전 문제가 발생할 수 있는 상황에 대해 생각해 보십시오.

여러 스레드가 동시에 전역 변수에 쓰면 다른 스레드의 방해를 받아 스레드 안전 문제가 발생할 수 있습니다.

public class ThreadCount implements Runnable {
    
    
    private static Integer count = 100;

    @Override
    public void run() {
    
    
        while (count > 1) {
    
    
            cal();
        }
    }

    private  void cal() {
    
    
        try {
    
    
            Thread.sleep(20);
        } catch (Exception e) {
    
    

        }
        count--;
        System.out.println(Thread.currentThread().getName() + "," + count);
    }


    public static void main(String[] args) {
    
    
        ThreadCount threadCount = new ThreadCount();
        Thread thread1 = new Thread(threadCount);
        Thread thread2 = new Thread(threadCount);
        thread1.start();
        thread2.start();
    }
}

현재 코드에서는 두 개의 스레드를 시작하는데, 두 스레드 모두 cal() 메소드를 실행하고 전역변수 count를 연산하게 되는데 동시에 동작하기 때문에 어떤 thread1 스레드가 먼저 실행될지, thread2 스레드가 먼저 실행될지는 알 수 없습니다. 스레드가 해제되면 스레드 안전성 문제가 발생합니다.

스레드 안전 문제 해결

생각: 스레드 안전 문제를 해결하는 방법

핵심 아이디어: 동기화 잠금을 사용하든 분산 잠금을 사용하든 잠금은 실제로 현재 스레드가 다른 스레드의 영향을 받지 않도록 현재 영역을 잠그는 것입니다. 그런 다음 실제 개발에서 수행하는 작업을 생각해 보세요. 블록 코드를 잠가야 합니다. 예를 들어 위의 코드를 예로 들어 보겠습니다.

휴식

추천

출처blog.csdn.net/weixin_51966599/article/details/127320500