>  기사  >  Java  >  Java 멀티스레딩에 대한 자세한 설명

Java 멀티스레딩에 대한 자세한 설명

PHP中文网
PHP中文网원래의
2018-05-15 10:38:572520검색

멀티스레딩 배경 지식 소개

멀티스레딩을 사용하면 모델을 단순화하고 강력한 코드를 작성할 수 있지만, 멀티스레딩을 작성하는 것은 쉽지 않으며 장기적인 연습 과정이 필요합니다.

멀티스레딩의 기본 개념 소개

프로세스와 스레드

① 프로세스: 프로그램(작업)의 실행 프로세스입니다. Dynamics

는 리소스(공유 메모리, 공유 파일)와 스레드를 보유합니다. Carrier

예: Eclipse, QQ

② 스레드:

Eclipse: 소스 코드 텍스트 편집, 소스 코드 컴파일, 구문 검증.

QQ: 문자 채팅, 파일 보내기 및 받기.

과정을 수업에 비유한다면 이 수업의 모든 학생은 스레드입니다. 학생은 학급의 가장 작은 단위이며 학급의 가장 작은 단위를 구성합니다. 한 학급에는 여러 명의 학생이 있을 수 있으며, 이 학생들은 모두 수업에서 동일한 테이블, 의자, 칠판 및 분필을 사용합니다.

스레드는 시스템에서 가장 작은 실행 단위입니다. 동일한 프로세스에는 여러 개의 스레드가 있으며 스레드는 프로세스의 리소스를 공유합니다.

스레드의 상호 작용

상호 배제 및 동기화.

Java 스레드의 첫 경험

Java.lang

class Thread

interface Runnable

public void run()

스레드의 일반적인 방법

현재 실행중인 스레드 릴리스 프로세서 리소스 정적 스레드 currentThread()현재 실행 중인 스레드 참조 반환

category

메서드 서명

소개

스레드 생성

Thread()

Thread(문자열 이름)

Thread(실행 가능한 대상 )

Thread(실행 가능한 대상, 문자열 이름)

스레드 메소드

void start()

스레드 시작

static void sleep(long millis)

thread sleep

static void sleep(long millis, int nanos)

void Join()

은 다른 스레드가 대기하도록 만듭니다. 현재 스레드 종료 oidVoid 조인 (Long Millis)

Void 조인 (Long Millis, int Nanos)

Static void aviled ()

스레드 참조 가져오기

두 스레드가 아무런 처리도 하지 않을 때는 교대로 실행됩니다.

부울 유형을 사용하여 스레드의 루프를 제어하는 ​​경우 변수 앞에 휘발성 키워드를 추가합니다. Volatile은 스레드가 다른 스레드가 작성한 값을 올바르게 읽을 수 있도록 보장합니다.

참고:

sleep() 메서드의 기능: 지정된 시간 동안 스레드를 절전 모드로 만듭니다.

join() 메소드의 역할: // 다른 스레드가 현재 스레드가 실행을 완료할 때까지 기다리게 합니다.

멀티 스레딩 사례

예 1:

1 package com.czgo;  
2   
3    
4   
5 /**  
6   
7  * 线程先生  
8   
9  * @author 疯子 
10  
11  * 
12  
13  */ 
14  
15 public class Actor extends Thread { 
16  
17     @Override 
18  
19     public void run() { 
20  
21         //getName():获取当前线程的名称 
22  
23         System.out.println(getName()+"是一个演员!"); 
24  
25         //用来记录线程跑的次数 
26  
27         int count = 0; 
28  
29         boolean keepRunning = true; 
30  
31         while(keepRunning){ 
32  
33             System.out.println(getName()+"登台演出"+(++count)); 
34  
35             if(count==100){ 
36  
37                 keepRunning = false; 
38  
39             } 
40  
41             if(count%10==0){ 
42  
43                 try { 
44  
45                     Thread.sleep(1000); 
46  
47                 } catch (InterruptedException e) { 
48  
49                     e.printStackTrace(); 
50  
51                 } 
52  
53             } 
54  
55         } 
56  
57         System.out.println(getName()+"的演出结束了!"); 
58  
59     } 
60  
61     
62  
63     public static void main(String[] args) { 
64  
65         Thread actor = new Actor(); 
66  
67         //setName:设置线程的名称 
68  
69         actor.setName("Mr.Thread"); 
70  
71         //启动线程 
72  
73         actor.start(); 
74  
75         
76  
77         Thread actressThread = new Thread(new Actress(),"Ms.Runnable"); 
78  
79         actressThread.start(); 
80  
81     } 
82  
83 } 
84  
85   
86  
87 class Actress implements Runnable{ 
88  
89     @Override 
90  
91     public void run() { 
92  
93         //getName():获取当前线程的名称 
94  
95         //currentThread()获取当前线程的引用 
96  
97         System.out.println(Thread.currentThread().getName()+"是一个演员!"); 
98  
99         //用来记录线程跑的次数
100 
101         int count = 0;
102 
103         boolean keepRunning = true;
104 
105         while(keepRunning){
106 
107             System.out.println(Thread.currentThread().getName()+"登台演出"+(++count));
108 
109             if(count==100){
110 
111                 keepRunning = false;
112 
113             }
114 
115             if(count%10==0){
116 
117                 try {
118 
119                     Thread.sleep(1000);
120 
121                 } catch (InterruptedException e) {
122 
123                     e.printStackTrace();
124 
125                 }
126 
127             }
128 
129         }
130 
131         System.out.println(Thread.currentThread().getName()+"的演出结束了!");   
132 
133     }134 135 }

예 2:

군대:

 1 package com.czgo; 
 2  
 3   
 4  
 5 /** 
 6  
 7  * 军队线程 
 8  
 9  * 模拟作战双方的行为
 10 
 11  * @author 疯子
 12 
 13  *
 14 
 15  */
 16 
 17 public class ArmyRunnable implements Runnable {
 18 
 19    
 20 
 21     //volatile保证了线程可以正确的读取其他线程写入的值
 22 
 23     //可见性 ref JMM,happens-before
 24 
 25     volatile boolean keepRunning = true;
 26 
 27    
 28 
 29     @Override
 30 
 31     public void run() {
 32 
 33        
 34 
 35         while(keepRunning){
 36 
 37             //发动5连击
 38 
 39             for(int i=0;i<5;i++){
 40 
 41                 System.out.println(Thread.currentThread().getName()+"进攻对方["+i+"]");
 42 
 43                 //让出了处理器时间,下次谁进攻还不一定呢!
 44 
 45                 Thread.yield();
 46 
 47             }
 48 
 49         }
 50 
 51        
 52 
 53         System.out.println(Thread.currentThread().getName()+"结束了战斗!");
 54 
 55        
 56 
 57     }
 58 
 59 }

핵심 인물:

1 package com.czgo; 
2  
3   
4  
5 /** 
6  
7  * 关键人物 
8  
9  * @author 疯子
10 
11  *
12 
13  */
14 
15 public class KeyPersonThread extends Thread {
16 
17     @Override
18 
19     public void run() {
20 
21         System.out.println(Thread.currentThread().getName()+"开始了战斗!"); 
22 
23         for(int i=0;i<10;i++){
24 
25             System.out.println(Thread.currentThread().getName()+"左突右杀,攻击随军...");
26 
27         }
28 
29         System.out.println(Thread.currentThread().getName()+"结束了战斗!");
30 
31  
32 
33     }
34 
35 }

단계:

  1 package com.czgo;  
  2   
  3    
  4   
  5 /**  
  6   
  7  * 隋唐演义大戏舞台  
  8   
  9  * @author win7 
  10  
  11  * 
  12  
  13  */ 
  14  
  15 public class Stage extends Thread { 
  16  
  17     
  18  
  19     @Override 
  20  
  21     public void run() { 
  22  
  23         System.out.println("欢迎观看隋唐演义"); 
  24  
  25         
  26  
  27         try { 
  28  
  29             Thread.sleep(5000); 
  30  
  31         } catch (InterruptedException e2) { 
  32  
  33             e2.printStackTrace(); 
  34  
  35         } 
  36  
  37         
  38  
  39         System.out.println("大幕徐徐拉开"); 
  40  
  41         
  42  
  43         try { 
  44  
  45             Thread.sleep(5000); 
  46  
  47         } catch (InterruptedException e2) { 
  48  
  49             e2.printStackTrace(); 
  50  
  51         } 
  52 
  53         
  54  
  55         System.out.println("话说隋朝末年,隋军与农民起义军杀得昏天暗地..."); 
  56  
  57         
  58  
  59         //隋朝军队 
  60  
  61         ArmyRunnable armyTaskOfSuiDynasty = new ArmyRunnable(); 
  62 
  63         //农民起义军 
  64  
  65         ArmyRunnable armyTaskOfRevolt = new ArmyRunnable(); 
  66  
  67         
  68  
  69         //使用Runnable接口创建线程 
  70  
  71         Thread armyOfSuiDynasty = new Thread(armyTaskOfSuiDynasty,"隋军"); 
  72  
  73         Thread armyOfSuiRevolt = new Thread(armyTaskOfRevolt,"农民起义军"); 
  74  
  75         
  76  
  77         //启动线程,让军队开始作战 
  78  
  79         armyOfSuiDynasty.start(); 
  80  
  81         armyOfSuiRevolt.start(); 
  82  
  83         
  84  
  85         //舞台线程休眠,大家专心观看军队的厮杀 
  86  
  87         try { 
  88  
  89             //Thread会指向当前类的线程 
  90 
  91             Thread.sleep(50); 
  92  
  93         } catch (InterruptedException e) { 
  94  
  95             e.printStackTrace(); 
  96  
  97         } 
  98
  99         System.out.println("正当双方激战正酣,半路杀出了个程咬金");
  100 
  101        
  102 
  103         Thread mrCheng = new KeyPersonThread();
  104 
  105         mrCheng.setName("程咬金");
  106 
  107        
  108 
  109         System.out.println("程咬金的理想就是结束战争,使百姓安居乐业!");
  110 
  111        
  112 
  113         //停止军队作战
  114 
  115         //停止线程的方法
  116 
  117         armyTaskOfSuiDynasty.keepRunning=false;
  118 
  119         armyTaskOfRevolt.keepRunning=false;
  120 
  121       
  122 
  123         try {
  124 
  125             Thread.sleep(2000);
  126 
  127         } catch (InterruptedException e1) {
  128 
  129             e1.printStackTrace();
  130 
  131         }
  132 
  133        134 135         //历史大戏留给关键人物
  136 
  137         mrCheng.start();
  138 
  139        
  140 
  141         try {
  142 
  143             //使其他线程等待当前线程执行完毕
  144 
  145             mrCheng.join();
  146 
  147         } catch (InterruptedException e) {
  148 
  149             e.printStackTrace();150 151         }
  152 
  153        
  154 
  155         System.out.println("战争结束,人民安居乐业,程先生实现了积极的人生梦想,为人民作出了贡献!");
  156 
  157         System.out.println("谢谢观看隋唐演义,再见!");
  158 
  159     }
  160 
  161    
  162 
  163     public static void main(String[] args) {
  164 
  165         new Stage().start();
  166 
  167     }
  168 
  169 }

스레드의 올바른 중지

올바르게 멈추는 방법 Java 스레드:

부울 유형을 통해 루프 종료를 제어할 수 있습니다.

not stop 메소드

stop() 메소드는 스레드를 갑자기 중지합니다.

stop() 메서드는 스레드를 중지하는 잘못된 방법입니다.

스레드의 상호 작용

Race Condition

여러 스레드가 동시에 동일한 데이터(메모리 영역)에 대한 접근을 공유하는 경우, 각 스레드가 데이터를 연산하려고 하여 데이터가 손상(corrupted)되는 현상 , 이러한 현상을 경쟁 조건이라고 합니다.

상호 배제란 무엇인가요?

상호 배제는 어떻게 이루어지나요?

상호 배타적인 하나의 스레드로만 액세스할 수 있습니다.

상호 배제 구현: 동기화된(내재적 잠금) 잠금.

동기화 구현: wait()/notify()/notifyAll(). V Java 동시성에 대한 지식을 확장하는 방법

Java 메모리 모드

JMM은 Java 스레드가 메모리를 통해 상호 작용하는 방법을 설명합니다.

Happens-BEFORE

SYNCHRONIZED, 휘발성 및 잠금 및 조건

Java 잠금 메커니즘 및 대기 대기 조건 조건

                      Java.util.concurrent.locks 구현

스레드 안전성                                       사용 사용 사용 사용 사용                  s ‐         ‐ ‐ ‐ ‐ to

                       DeadLocks

멀티 스레드 프로그래밍을 위한 공통 상호 작용 모델

생산자- 소비자 모델 re 읽기-쓰기 잠금 모델

미래 모델

작업자 스레드 모델

Java.util.CURRENT

스레드 풀 Executorservice

미래

EUE

에서 권장하는 두 권의 책:

Core Java

Java 실제 동시성

두 가지 스레드 생성 방법 비교

Thread:

① Thread 클래스 상속;

Runnable:

① Runnable 메서드는 Java 단일 상속 기능으로 인해 Thread 메서드를 피할 수 있습니다. 결함.

② 실행 가능한 코드는 여러 스레드(스레드 인스턴스)에서 공유할 수 있으며, 이는 여러 스레드가 동일한 리소스를 처리하는 상황에 적합합니다.

Case:

Thread:
 1 package com.czgo; 
 2  
 3   
 4  
 5 class MyThread extends Thread{ 
 6  
 7     
 8  
 9     private int ticketsCont = 5;    //一共有5张火车票
 10 
 11    
 12 
 13     private String name;            //窗口,也即是线程的名字
 14 
 15    
 16 
 17     public MyThread(String name){
 18 
 19         this.name = name;
 20 
 21     }
 22 
 23    24 25     @Override
 26 
 27     public void run() {
 28 
 29        30 31         while(ticketsCont>0){
 32 
 33             ticketsCont--;      //如果还有票,就卖掉一张
 34 
 35             System.out.println(name+"卖了1张票,剩余票数为:"+ticketsCont);
 36 
 37         }
 38 
 39     }
 40 
 41    
 42 
 43 }
 44 
 45  
 46 
 47 public class TicketsThread {
 48 
 49  
 50 
 51     public static void main(String[] args) {
 52 
 53         //创建3个线程,模拟三个窗口卖票
 54 
 55         MyThread mt1 = new MyThread("窗口1");
 56 
 57         MyThread mt2 = new MyThread("窗口2");
 58 
 59         MyThread mt3 = new MyThread("窗口3");
 60 
 61        62 63         //启动这三个线程,也即是窗口,开始卖票
 64 
 65         mt1.start();
 66 
 67         mt2.start();
 68 
 69         mt3.start();
 70 
 71        
 72 
 73     }
 74 
 75  
 76 
 77 }

Runnable:

 1 package com.czgo; 
 2  
 3   
 4  
 5 class MyThread implements Runnable{ 
 6  
 7     
 8  
 9     private int ticketsCont = 5;    //一共有5张火车票
 10 
 11  
 12 
 13     @Override
 14 
 15     public void run() {
 16 
 17        
 18 
 19         while(ticketsCont>0){
 20 
 21             ticketsCont--;      //如果还有票,就卖掉一张
 22 
 23             System.out.println(Thread.currentThread().getName()+"卖了1张票,剩余票数为:"+ticketsCont);
 24 
 25         }
 26 
 27        
 28 
 29     }
 30 
 31    
 32 
 33 }
 34 
 35  
 36 
 37 public class TicketsRunnable {
 38 
 39  
 40 
 41     public static void main(String[] args) {
 42 
 43        
 44 
 45         MyThread mt1 = new MyThread();
 46 
 47         MyThread mt2 = new MyThread();
 48 
 49         MyThread mt3 = new MyThread();
 50 
 51        
 52 
 53         //创建三个线程来模拟三个售票窗口
 54 
 55         Thread th1 = new Thread(mt1,"窗口1");
 56 
 57         Thread th2 = new Thread(mt2,"窗口2");
 58 
 59         Thread th3 = new Thread(mt3,"窗口3");
 60 
 61        
 62 
 63         //启动这三个线程,也即是三个窗口开始卖票
 64 
 65         th1.start();
 66 
 67         th2.start();
 68 
 69         th3.start();
 70 
 71  
 72 
 73     }
 74 
 75  
 76 
 77 }

Thread 수명 주기

Illustration:

Creation: Thread thd = new Thread()와 같은 새 스레드 개체를 만듭니다.

Ready: 스레드 객체를 생성한 후 스레드의 start() 메서드가 호출됩니다. (참고: 현재 스레드는 스레드 대기열에 진입하여 CPU 서비스를 얻기를 기다리고 있으며 실행 조건이 있지만 반드시 실행을 시작한 것은 아닙니다).

실행 중: 준비 상태의 스레드가 CPU 리소스를 획득하면 실행 상태로 전환되고 run() 메서드의 논리 실행을 시작합니다.

종료: 스레드의 run() 메서드가 실행되거나 스레드가 stop() 메서드를 호출한 후 스레드는 종료된 상태로 들어갑니다. 블로킹: 특정 상황에서 실행 스레드는 어떤 이유로 일시적으로 CPU 리소스를 포기하고 자체 실행을 일시 중단하며 sleep() 메서드 호출과 같은 차단 상태에 들어갑니다.

스레드의 수호 성인 - 데몬 스레드

Java 스레드에는 두 가지 유형이 있습니다.

사용자 스레드: 포그라운드에서 실행되고 특정 작업을 수행합니다.

예를 들어 프로그램의 메인 스레드, 네트워크에 연결된 하위 스레드 등은 모두 사용자 스레드입니다.

데몬 스레드: 백그라운드에서 실행되고 다른 전경 스레드를 제공합니다.

특징: 모든 사용자 스레드의 실행이 완료되면 데몬 스레드가 JVM과 함께 작업을 종료합니다.

애플리케이션: 데이터베이스 연결 풀의 스레드 모니터링, JVM 가상 머신이 시작된 후 스레드 모니터링.

가장 일반적인 데몬 스레드: 가비지 수집 스레드.

如何设置守护线程

可以通过调用Thread类的setDaemon(true)方法来设置当前线程为守护线程。

注意事项:

setDaemon(true)必须在start()方法之前调用,否则会抛出IllegalThreadStateException异常。

在守护线程中产生的新线程也是守护线程

不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑。

案例:

  1 package com.czgo;  
  2   
  3    
  4   
  5 import java.io.File;  
  6   
  7 import java.io.FileOutputStream;  
  8   
  9 import java.io.IOException; 
  10  
  11 import java.io.OutputStream; 
  12  
  13 import java.util.Scanner; 
  14  
  15   
  16  
  17 class DaemonThread implements Runnable{ 
  18  
  19     @Override 
  20  
  21     public void run() { 
  22  
  23         System.out.println("进入守护线程"+Thread.currentThread().getName()); 
  24  
  25         try { 
  26  
  27             writeToFile(); 
  28  
  29         } catch (IOException e) { 
  30  
  31             e.printStackTrace(); 
  32  
  33         } catch (InterruptedException e) { 34  35             e.printStackTrace(); 
  36  
  37         } 
  38  
  39         System.out.println("退出守护线程"+Thread.currentThread().getName()); 
  40  
  41         
  42  
  43     } 
  44  
  45     
  46  
  47     private void writeToFile() throws IOException, InterruptedException{ 
  48  
  49         File filename = new File("C:\\ide"+File.separator+"daemon.txt"); 
  50  
  51         OutputStream os = new FileOutputStream(filename,true); 
  52  
  53         int count = 0; 
  54  
  55         while(count<999){ 
  56  
  57             os.write(("\r\nword"+count).getBytes()); 
  58  
  59             System.out.println("守护线程"+Thread.currentThread().getName()+"向文件中写入了word"+count++); 
  60 
  61             Thread.sleep(1000); 
  62  
  63         } 
  64  
  65         os.close(); 
  66  
  67     } 
  68  
  69 } 
  70  
  71   
  72  
  73 public class DaemonThreadDemo { 
  74  
  75   
  76  
  77     public static void main(String[] args) { 
  78  
  79         
  80  
  81         System.out.println("进入主线程"+Thread.currentThread().getName()); 
  82  
  83         DaemonThread daemonThread = new DaemonThread(); 
  84  
  85         Thread thread = new Thread(daemonThread); 
  86  
  87         thread.setDaemon(true); 
  88  
  89         thread.start(); 
  90  
  91         
  92  
  93         Scanner sc = new Scanner(System.in); 
  94  
  95         sc.next();
  96  
  97         
  98  
  99         System.out.println("退出主线程"+Thread.currentThread().getName());
  100 
  101     }
  102 
  103  
  104 
  105 }


使用jstack生成线程快照

jstack

作用:生成jvm当前时刻线程的快照(threaddump,即当前进程中所有线程的信息)

目的:帮助定位程序问题出现的原因,如长时间停顿、cpu占用率过高等。

如何使用jstack

jstack –l pid

 

内存可见性

可见性介绍

可见性:一个线程对共享变量值的修改,能够及时地被其他线程看到。

共享变量:如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量。

Java内存模型(JMM)

Java内存模型(Java Memory Model)描述了程序中各种变量(线程共享变量)的访问规则,以及在Java中将变量存储到内存和从内存中读取出变量这样的底层细节。

所有变量都存储在主内存中

每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝)。

 

线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写。

不同线程之间无法直接访问其他线程工作内存中的变量,线程间变量值的传递需要通过主内存来完成。

共享变量可见性实现的原理

线程1对共享变量的修改要想被线程2及时看到,必须要经过如下2个步骤:

把工作内存1中更新过的共享变量刷新到主内存中。

将主内存中最新的共享变量的值更新到工作内存2中。

可见性

要实现共享变量的可见性,必须保证两点:

线程修改后的共享变量值能够及时从工作内存刷新到主内存中。

其他线程能够及时把共享变量的最新值从主内存更新到自己的工作内存中。

可见性的实现方式

Java语言层面支持的可见性实现方式:

Synchronized

Volatile

Synchronized实现可见性

Synchronized能够实现:

原子性(同步);

可见性

JMM关于Synchronized的两条规定:

线程解锁前,必须把共享变量的最新值刷新到主内存中;

线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(注意:加锁与解锁需要是同一把锁)

线程解锁前对共享变量得修改在下次加锁时对其他线程可见。

线程执行互斥代码的过程:

  1. 获得互斥锁

  2. 清空工作内存

  3. 从主内存中拷贝变量的最新副本到工作内存

  4. 执行代码。

  5. 将更改后的共享变量的值刷新到主内存。

  6. 释放互斥锁。

重排序

重排序:代码书写的顺序与实际执行的顺序不同,指令重排序是编译器或处理器为了提高程序性能而做的优化。

  1. 编译器优化的重排序(编译器优化)。

  2. 指令级并行重排序(处理器优化)。

  3. 内存系统的重排序(处理器优化)。

as-if-serial

as-if-serial:无论如何重排序,程序执行的结果应该与代码顺序执行的结果一致(java编译器、运行时和处理器都会保证Java在单线程下遵循as-if-serial语义)。

例子:

Int num = 1;

Int num2 = 2;

Int sum = num+num2;

单线程:第1、2行的顺序可以重排,但第3行不能

重排序不会给单线程带来内存可见性问题

多线程中程序交错执行时,重排序可能会造成内存可见性问题。

Volatile实现可见性

Volatile关键字:

能够保证volatile变量的可见性

不能保证volatile变量复合操作的原子性

Volatile如何实现内存可见性:

심층적으로: 이는 메모리 장벽을 추가하고 재정렬 최적화를 비활성화하여 달성됩니다.

휘발성 변수에 쓰기 작업이 수행되면 쓰기 작업 후에 저장 배리어 명령이 추가됩니다.

휘발성 변수에 읽기 작업이 수행되면 읽기 작업 전에 로드 배리어 명령이 추가됩니다.

일반인의 용어로 말하면, 휘발성 변수는 매번 추가됩니다. 스레드가 액세스할 때 변수 값은 강제로 메인 메모리에서 다시 읽혀집니다. 변수가 변경되면 스레드는 강제로 최신 값을 메인 메모리로 새로 고쳐야 합니다. 메모리. 이러한 방식으로 다른 스레드는 언제든지 변수의 최신 값을 볼 수 있습니다.

스레드가 휘발성 변수를 쓰는 과정:

  1. 스레드의 작업 메모리에서 휘발성 변수 복사본의 값을 변경합니다.

  2. 변경된 복사본의 값을 작업 메모리에서 메인 메모리로 새로 고칩니다.

휘발성 변수를 읽는 스레드 프로세스:

  1. 메인 메모리에서 휘발성 변수의 최신 값을 스레드의 작업 메모리로 읽습니다.

  2. 작업 메모리에서 휘발성 변수의 복사본을 읽습니다.

휘발성이 적용되는 경우

멀티 스레드에서 휘발성 변수를 안전하게 사용하려면 다음도 충족해야 합니다.

1. 변수에 대한 쓰기 작업은 현재 값에 의존하지 않습니다

2. 온도를 기록하는 데 사용되는 부울

3. 이 변수는 다른 변수와의 불변성에 포함되지 않습니다.

결론: 뒤에서 다른 사람들이 당신에 대해 어떻게 생각하는지 신경 쓰지 마십시오. 왜냐하면 이러한 말은 사실을 바꿀 수는 없지만 마음을 어지럽힐 수 있기 때문입니다.

위 내용은 Java 멀티스레딩에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.