这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
分享:技术:多线程:多线程编程核心技术整理 [2016/05/28 14:36] gxx |
分享:技术:多线程:多线程编程核心技术整理 [2016/06/19 18:16] (当前版本) gxx |
||
---|---|---|---|
行 10: | 行 10: | ||
* 线程的优点 | * 线程的优点 | ||
多线程或者说多任务操作系统,可以最大限度地利用CPU的空闲时间在不同的任务之间不停的切换,由于切换的速度非常快,使使用者感觉多个任务是在同时进行的。当然后来也出现多个CPU,或者一个CPU多核就是为了加快机器运算。 | 多线程或者说多任务操作系统,可以最大限度地利用CPU的空闲时间在不同的任务之间不停的切换,由于切换的速度非常快,使使用者感觉多个任务是在同时进行的。当然后来也出现多个CPU,或者一个CPU多核就是为了加快机器运算。 | ||
+ | ==== 使用多线程 ==== | ||
+ | === 打印主线程的名字 === | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test1; | ||
+ | /** | ||
+ | * 测试类-打印主线程的名字 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | /** | ||
+ | * 打印main方法当前线程名字 | ||
+ | * 输出:main | ||
+ | */ | ||
+ | System.out.println(Thread.currentThread().getName()); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | main | ||
+ | </code> | ||
+ | === 两种实现线程类的方法 === | ||
+ | * 继承Thread实现线程类 | ||
+ | <code java MyThread1.java> | ||
+ | package com.gxx.threads.study.test2; | ||
+ | /** | ||
+ | * 继承Thread实现线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread1 extends Thread { | ||
+ | /** | ||
+ | * 覆盖Thread中的run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | System.out.println("MyThread1.run()"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | * 实现Runnable实现线程类 | ||
+ | <code java MyThread2.java> | ||
+ | package com.gxx.threads.study.test2; | ||
+ | |||
+ | /** | ||
+ | * 实现Runnable实现线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread2 implements Runnable { | ||
+ | /** | ||
+ | * 实现Runnable中的run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | System.out.println("MyThread2.run()"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | * 两种实现的区别 | ||
+ | 本质上并没有什么区别,但是由于JAVA只支持单继承,如果继承了Thread就不能继承其它的父类了,所以绝大多数情况下是由于这个,而Thread类其实也实现了Runnable接口。 | ||
+ | === 测试start()、run()和多次start() === | ||
+ | * start() | ||
+ | start()通过“线程规划器”此线程已经准备就绪,等待调用run(),CPU在不确定的时间调用run() | ||
+ | * run() | ||
+ | 直接执行run,就不是“线程规划器”来调度了,而是执行run()的当前线程直接调用方法,如果是main()中调用,则当前线程就是main | ||
+ | * 多次start() | ||
+ | 多次start()会抛出异常java.lang.IllegalThreadStateException,报线程状态异常 | ||
+ | <code java MyThread.java> | ||
+ | package com.gxx.threads.study.test3; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 构造方法 | ||
+ | */ | ||
+ | public MyThread(){ | ||
+ | System.out.println("线程[" + Thread.currentThread().getName() + "]执行构造方法"); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | System.out.println("线程[" + Thread.currentThread().getName() + "]执行run"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test3; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试start和run和多次start | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | MyThread thread = new MyThread(); | ||
+ | thread.setName("线程A");//设置线程名字 | ||
+ | thread.run(); | ||
+ | thread.start(); | ||
+ | thread.start(); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | 线程[main]执行构造方法 | ||
+ | 线程[main]执行run | ||
+ | 线程[线程A]执行run | ||
+ | Exception in thread "main" java.lang.IllegalThreadStateException | ||
+ | at java.lang.Thread.start(Thread.java:705) | ||
+ | at com.gxx.threads.study.test3.Test.main(Test.java:17) | ||
+ | </code> | ||
+ | === 不共享变量,线程安全 === | ||
+ | <code java MyThread.java> | ||
+ | package com.gxx.threads.study.test4; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 计数 | ||
+ | */ | ||
+ | int count = 10; | ||
+ | |||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | System.out.println("线程[" + Thread.currentThread().getName() + "]执行count=" + (count--)); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java TestSafe.java> | ||
+ | package com.gxx.threads.study.test4; | ||
+ | |||
+ | /** | ||
+ | * 测试类-不共享变量,线程安全 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class TestSafe { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | MyThread thead1 = new MyThread(); | ||
+ | MyThread thead2 = new MyThread(); | ||
+ | MyThread thead3 = new MyThread(); | ||
+ | MyThread thead4 = new MyThread(); | ||
+ | MyThread thead5 = new MyThread(); | ||
+ | MyThread thead6 = new MyThread(); | ||
+ | MyThread thead7 = new MyThread(); | ||
+ | MyThread thead8 = new MyThread(); | ||
+ | MyThread thead9 = new MyThread(); | ||
+ | MyThread thead10 = new MyThread(); | ||
+ | thead1.start(); | ||
+ | thead2.start(); | ||
+ | thead3.start(); | ||
+ | thead4.start(); | ||
+ | thead5.start(); | ||
+ | thead6.start(); | ||
+ | thead7.start(); | ||
+ | thead8.start(); | ||
+ | thead9.start(); | ||
+ | thead10.start(); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | 线程[Thread-0]执行count=10 | ||
+ | 线程[Thread-2]执行count=10 | ||
+ | 线程[Thread-1]执行count=10 | ||
+ | 线程[Thread-3]执行count=10 | ||
+ | 线程[Thread-4]执行count=10 | ||
+ | 线程[Thread-5]执行count=10 | ||
+ | 线程[Thread-6]执行count=10 | ||
+ | 线程[Thread-7]执行count=10 | ||
+ | 线程[Thread-8]执行count=10 | ||
+ | 线程[Thread-9]执行count=10 | ||
+ | </code> | ||
+ | === 共享变量,线程不安全 === | ||
+ | <code java TestNotSafe.java> | ||
+ | package com.gxx.threads.study.test4; | ||
+ | |||
+ | /** | ||
+ | * 测试类-共享变量,线程不安全 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class TestNotSafe { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | MyThread thread = new MyThread(); | ||
+ | Thread thead1 = new Thread(thread); | ||
+ | Thread thead2 = new Thread(thread); | ||
+ | Thread thead3 = new Thread(thread); | ||
+ | Thread thead4 = new Thread(thread); | ||
+ | Thread thead5 = new Thread(thread); | ||
+ | Thread thead6 = new Thread(thread); | ||
+ | Thread thead7 = new Thread(thread); | ||
+ | Thread thead8 = new Thread(thread); | ||
+ | Thread thead9 = new Thread(thread); | ||
+ | Thread thead10 = new Thread(thread); | ||
+ | thead1.start(); | ||
+ | thead2.start(); | ||
+ | thead3.start(); | ||
+ | thead4.start(); | ||
+ | thead5.start(); | ||
+ | thead6.start(); | ||
+ | thead7.start(); | ||
+ | thead8.start(); | ||
+ | thead9.start(); | ||
+ | thead10.start(); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | 线程[Thread-2]执行count=10 | ||
+ | 线程[Thread-3]执行count=9 | ||
+ | 线程[Thread-1]执行count=10 | ||
+ | 线程[Thread-4]执行count=8 | ||
+ | 线程[Thread-5]执行count=7 | ||
+ | 线程[Thread-6]执行count=6 | ||
+ | 线程[Thread-7]执行count=5 | ||
+ | 线程[Thread-8]执行count=4 | ||
+ | 线程[Thread-9]执行count=3 | ||
+ | 线程[Thread-10]执行count=2 | ||
+ | </code> | ||
+ | === 共享变量,使用synchronized使得线程安全 === | ||
+ | <code java Tools.java> | ||
+ | package com.gxx.threads.study.test5; | ||
+ | |||
+ | /** | ||
+ | * 工具类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Tools { | ||
+ | /** | ||
+ | * 计数 | ||
+ | */ | ||
+ | int count = 10; | ||
+ | |||
+ | /** | ||
+ | * 打印count | ||
+ | * @throws Exception | ||
+ | */ | ||
+ | public synchronized void printCount() { | ||
+ | try { | ||
+ | count--; | ||
+ | /** | ||
+ | * 睡眠1秒 | ||
+ | */ | ||
+ | Thread.sleep(1000); | ||
+ | System.out.println("线程[" + Thread.currentThread().getName() + "]执行count=" + count); | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java MyThread.java> | ||
+ | package com.gxx.threads.study.test5; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 工具类 | ||
+ | */ | ||
+ | Tools tools; | ||
+ | |||
+ | /** | ||
+ | * 构造方法 | ||
+ | * @param tools | ||
+ | */ | ||
+ | public MyThread(Tools tools){ | ||
+ | this.tools = tools; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * 覆盖父类的run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | tools.printCount(); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test5; | ||
+ | |||
+ | /** | ||
+ | * 测试类-共享变量,使用synchronized使得线程安全 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | Tools tools = new Tools(); | ||
+ | /** | ||
+ | * 共用同一个tools | ||
+ | */ | ||
+ | MyThread thread1 = new MyThread(tools); | ||
+ | MyThread thread2 = new MyThread(tools); | ||
+ | thread1.start(); | ||
+ | thread2.start(); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | 线程[Thread-0]执行count=9 | ||
+ | 线程[Thread-1]执行count=8 | ||
+ | </code> | ||
+ | === count--和System.out.println() === | ||
+ | 注意: | ||
+ | * count--不是同步的,分三步1.获取count的值;2.count-1;3.再赋值给count;不同步则有时间差 | ||
+ | * System.out.println()是同步的 | ||
+ | <code java println.java> | ||
+ | public void println(String x) { | ||
+ | synchronized (this) { | ||
+ | print(x); | ||
+ | newLine(); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | ==== getId()、isAlive()和sleep() ==== | ||
+ | * getId 是取得线程的唯一标识 | ||
+ | * isAlive是判断当前线程是否处于活动状态 | ||
+ | * sleep是指this.currentThread()休眠多少毫秒 | ||
+ | <code java MyThread.java> | ||
+ | package com.gxx.threads.study.test6; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | System.out.println("线程 run 开始 时间=" + System.currentTimeMillis()); | ||
+ | System.out.println("线程 getId()=" + this.getId()); | ||
+ | System.out.println("线程 run 中 isAlive()=" + this.isAlive()); | ||
+ | System.out.println("线程 run 结束 时间=" + System.currentTimeMillis()); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test6; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试getId()、isAlive()和sleep() | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println("main主线程 开始 时间=" + System.currentTimeMillis()); | ||
+ | System.out.println("main主线程 getId()=" + Thread.currentThread().getId()); | ||
+ | MyThread thread = new MyThread(); | ||
+ | System.out.println("线程 start前 isAlive()=" + thread.isAlive()); | ||
+ | thread.start(); | ||
+ | System.out.println("线程 start后 isAlive()=" + thread.isAlive()); | ||
+ | System.out.println("main主线程 结束 时间=" + System.currentTimeMillis()); | ||
+ | |||
+ | try { | ||
+ | Thread.sleep(1000); | ||
+ | System.out.println("main主线程 sleep(1秒)后 线程 isAlive=" + thread.isAlive()); | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | main主线程 开始 时间=1464492826078 | ||
+ | main主线程 getId()=1 | ||
+ | 线程 start前 isAlive()=false | ||
+ | 线程 start后 isAlive()=true | ||
+ | main主线程 结束 时间=1464492826080 | ||
+ | 线程 run 开始 时间=1464492826080 | ||
+ | 线程 getId()=8 | ||
+ | 线程 run 中 isAlive()=true | ||
+ | 线程 run 结束 时间=1464492826080 | ||
+ | main主线程 sleep(1秒)后 线程 isAlive=false | ||
+ | </code> | ||
+ | ==== 停止线程 ==== | ||
+ | === interrupt、isInterrupted、interrupted === | ||
+ | * interrupt | ||
+ | 给线程打个中断的标记,线程继续运行 | ||
+ | * isInterrupted | ||
+ | 判断线程是否有中断的标记,如果有返回true,多次调用都返回true | ||
+ | * interrupted | ||
+ | 静态方法,Thread.interrupted(),判断当前线程是否有中断的标记,如果有返回true,并清空该标记,下次再执行,则返回false | ||
+ | <code java MyThread.java> | ||
+ | package com.gxx.threads.study.test7; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | System.out.println("run 开始"); | ||
+ | this.interrupt(); | ||
+ | System.out.println("执行this.interrupt()只是打个线程中断标记,线程还是继续往下走"); | ||
+ | System.out.println("this.isInterrupted()=" + this.isInterrupted()); | ||
+ | System.out.println("this.isInterrupted()=" + this.isInterrupted()); | ||
+ | System.out.println("执行静态方法Thread.interrupted()=" + Thread.interrupted() + "后清空中断状态"); | ||
+ | System.out.println("再执行静态方法Thread.interrupted()=" + Thread.interrupted()); | ||
+ | System.out.println("this.isInterrupted()=" + this.isInterrupted()); | ||
+ | System.out.println("run 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test7; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试interrupt、isInterrupted、interrupted | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println("main 开始"); | ||
+ | MyThread thread = new MyThread(); | ||
+ | thread.start(); | ||
+ | System.out.println("main 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | main 开始 | ||
+ | main 结束 | ||
+ | run 开始 | ||
+ | 执行this.interrupt()只是打个线程中断标记,线程还是继续往下走 | ||
+ | this.isInterrupted()=true | ||
+ | this.isInterrupted()=true | ||
+ | 执行静态方法Thread.interrupted()=true后清空中断状态 | ||
+ | 再执行静态方法Thread.interrupted()=false | ||
+ | this.isInterrupted()=false | ||
+ | run 结束 | ||
+ | </code> | ||
+ | === 测试线程interrupt()然后sleep() === | ||
+ | 先中断,再睡眠,会抛出异常 | ||
+ | <code java MyThread.java> | ||
+ | package com.gxx.threads.study.test8; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | System.out.println("run 开始"); | ||
+ | System.out.println("run 准备执行sleep(1000) 睡眠1秒"); | ||
+ | try { | ||
+ | sleep(1000); | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | System.out.println("run 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test8; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试线程interrupt()然后sleep() | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println("main 开始"); | ||
+ | MyThread thread = new MyThread(); | ||
+ | thread.start(); | ||
+ | thread.interrupt(); | ||
+ | System.out.println("main 执行thread.interrupt(),给thread打个中断标记"); | ||
+ | System.out.println("main 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | main 开始 | ||
+ | main 执行thread.interrupt(),给thread打个中断标记 | ||
+ | main 结束 | ||
+ | run 开始 | ||
+ | run 准备执行sleep(1000) 睡眠1秒 | ||
+ | run 结束 | ||
+ | java.lang.InterruptedException: sleep interrupted | ||
+ | at java.lang.Thread.sleep(Native Method) | ||
+ | at com.gxx.threads.study.test8.MyThread.run(MyThread.java:16) | ||
+ | |||
+ | </code> | ||
+ | === 测试线程sleep()然后interrupt() === | ||
+ | 在睡眠的过程中,中断,会抛出异常 | ||
+ | <code java MyThread.java> | ||
+ | package com.gxx.threads.study.test9; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | System.out.println("run 开始"); | ||
+ | System.out.println("run 准备执行sleep(10000) 睡眠10秒"); | ||
+ | try { | ||
+ | sleep(10000); | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | System.out.println("run 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test9; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试线程sleep()然后interrupt() | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println("main 开始"); | ||
+ | MyThread thread = new MyThread(); | ||
+ | thread.start(); | ||
+ | System.out.println("main 睡眠1秒"); | ||
+ | try { | ||
+ | Thread.sleep(1000); | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | System.out.println("main 执行thread.interrupt(),给thread打个中断标记"); | ||
+ | thread.interrupt(); | ||
+ | System.out.println("main 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | main 开始 | ||
+ | main 睡眠1秒 | ||
+ | run 开始 | ||
+ | run 准备执行sleep(10000) 睡眠10秒 | ||
+ | main 执行thread.interrupt(),给thread打个中断标记 | ||
+ | main 结束 | ||
+ | run 结束 | ||
+ | java.lang.InterruptedException: sleep interrupted | ||
+ | at java.lang.Thread.sleep(Native Method) | ||
+ | at com.gxx.threads.study.test9.MyThread.run(MyThread.java:16) | ||
+ | </code> | ||
+ | === 抛出异常或者return来停止线程 === | ||
+ | 建议使用这两种方式 | ||
+ | <code java> | ||
+ | if(this.isInterrupted()){ | ||
+ | throw new Exception(); | ||
+ | } | ||
+ | </code> | ||
+ | <code java> | ||
+ | if(this.interrupted()){ | ||
+ | return; | ||
+ | } | ||
+ | </code> | ||
+ | === stop()暴力停止,@Deprecated作废 === | ||
+ | 不建议使用,因为stop()停止线程有可能使一些清理性的工作得不到完成。另外会对锁定的对象强制解锁,导致数据得不到同步的处理,出现数据不一致的问题!比如: | ||
+ | <code java> | ||
+ | synchronized public void setUserNameAndPassword(String userName, String password){ | ||
+ | this.userName = userName; | ||
+ | Thread.sleep(1000000);//正好外面执行stop(),导致数据不一致 | ||
+ | this.password = password; | ||
+ | } | ||
+ | </code> | ||
+ | ==== suspend()和resume() ==== | ||
+ | * suspend()暂停 | ||
+ | 如果synchronized方法执行一半,不会释放锁,一直卡着,比如线程内System.out.println()打印一半(打印数据,再打印换行,有synchronized代码块)暂停了,其他地方调System.out.println()都会一直卡着,输出不到控台;@Deprecated作废,不建议使用 | ||
+ | * resume()恢复 | ||
+ | 从暂停的地方恢复,程序往下走,但是有时间差,可能会有脏数据;@Deprecated作废,不建议使用 | ||
+ | <code java MyThread.java>package com.gxx.threads.study.test10; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 标志 | ||
+ | */ | ||
+ | boolean flag = false; | ||
+ | |||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | System.out.println("run 开始"); | ||
+ | System.out.println("run 打印flag=" + flag); | ||
+ | System.out.println("run 执行this.suspend(),并等待resume()"); | ||
+ | this.suspend(); | ||
+ | System.out.println("run 线程被恢复"); | ||
+ | System.out.println("run 再次打印flag=" + flag); | ||
+ | System.out.println("run 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test10; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试线程suspend()和resume(),产生脏数据 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println("main 开始"); | ||
+ | MyThread thread = new MyThread(); | ||
+ | thread.start(); | ||
+ | System.out.println("main 睡眠1秒"); | ||
+ | try { | ||
+ | Thread.sleep(1000); | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | System.out.println("main 睡眠1秒后 修改thread.flag = true"); | ||
+ | thread.flag = true; | ||
+ | System.out.println("main 执行thread.resume()"); | ||
+ | thread.resume(); | ||
+ | System.out.println("main 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | main 开始 | ||
+ | main 睡眠1秒 | ||
+ | run 开始 | ||
+ | run 打印flag=false | ||
+ | run 执行this.suspend(),并等待resume() | ||
+ | main 睡眠1秒后 修改thread.flag = true | ||
+ | main 执行thread.resume() | ||
+ | main 结束 | ||
+ | run 线程被恢复 | ||
+ | run 再次打印flag=true | ||
+ | run 结束 | ||
+ | </code> | ||
+ | ==== yield() ==== | ||
+ | 让出CPU资源,但是有可能让出又马上获得CPU时间片。 | ||
+ | <code java MyThread.java> | ||
+ | package com.gxx.threads.study.test11; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 计数 | ||
+ | */ | ||
+ | int count = 10; | ||
+ | |||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | while(count > 0){ | ||
+ | System.out.println("[" + this.getName() + "] count=" + count); | ||
+ | count --; | ||
+ | this.yield();//让出CPU | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test11; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试线程yield()让出CPU | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println("main 开始"); | ||
+ | MyThread threadA = new MyThread(); | ||
+ | threadA.setName("线程A"); | ||
+ | threadA.start(); | ||
+ | MyThread threadB = new MyThread(); | ||
+ | threadB.setName("线程B"); | ||
+ | threadB.start(); | ||
+ | System.out.println("main 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | main 开始 | ||
+ | main 结束 | ||
+ | [线程A] count=10 | ||
+ | [线程B] count=10 | ||
+ | [线程A] count=9 | ||
+ | [线程B] count=9 | ||
+ | [线程A] count=8 | ||
+ | [线程B] count=8 | ||
+ | [线程A] count=7 | ||
+ | [线程B] count=7 | ||
+ | [线程A] count=6 | ||
+ | [线程B] count=6 | ||
+ | [线程A] count=5 | ||
+ | [线程B] count=5 | ||
+ | [线程A] count=4 | ||
+ | [线程B] count=4 | ||
+ | [线程A] count=3 | ||
+ | [线程B] count=3 | ||
+ | [线程A] count=2 | ||
+ | [线程B] count=2 | ||
+ | [线程A] count=1 | ||
+ | [线程B] count=1 | ||
+ | </code> | ||
+ | ==== 线程的优先级 ==== | ||
+ | * 取值[1]~[10]的整数,默认值位[5] | ||
+ | * 设置优先级setPriority() | ||
+ | * 获取优先级getPriority() | ||
+ | <code java MyThread.java> | ||
+ | package com.gxx.threads.study.test12; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | System.out.println("run 开始"); | ||
+ | System.out.println("线程[" + Thread.currentThread().getName() + "]优先级[继承][" + Thread.currentThread().getPriority() + "]"); | ||
+ | System.out.println("run 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test12; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试线程线程优先级的常量,默认值,范围和继承性 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println("===线程优先级常量=开始======"); | ||
+ | System.out.println("线程优先级MIN_PRIORITY=" + Thread.MIN_PRIORITY); | ||
+ | System.out.println("线程优先级NORM_PRIORITY=" + Thread.NORM_PRIORITY); | ||
+ | System.out.println("线程优先级MAX_PRIORITY=" + Thread.MAX_PRIORITY); | ||
+ | System.out.println("===线程优先级常量=结束======"); | ||
+ | |||
+ | System.out.println("main 开始"); | ||
+ | System.out.println("线程[" + Thread.currentThread().getName() + "]优先级[默认][" + Thread.currentThread().getPriority() + "]"); | ||
+ | |||
+ | try{ | ||
+ | System.out.println("========="); | ||
+ | System.out.println("线程[" + Thread.currentThread().getName() + "]优先级[设置][11]"); | ||
+ | Thread.currentThread().setPriority(11); | ||
+ | } catch (Exception e){ | ||
+ | System.out.println("设置[11]失败!"); | ||
+ | System.out.println("线程优先级设置范围在[" + Thread.MIN_PRIORITY + "]~[" + Thread.MAX_PRIORITY + "]"); | ||
+ | System.out.println("========="); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * range MIN_PRIORITY to MAX_PRIORITY | ||
+ | * 设置线程优先级必须在[MIN_PRIORITY,MAX_PRIORITY]也就是[1,10] | ||
+ | */ | ||
+ | Thread.currentThread().setPriority(8); | ||
+ | |||
+ | System.out.println("线程[" + Thread.currentThread().getName() + "]优先级[设置][" + Thread.currentThread().getPriority() + "]"); | ||
+ | |||
+ | MyThread thread = new MyThread(); | ||
+ | thread.setName("线程A"); | ||
+ | thread.start(); | ||
+ | System.out.println("线程[" + Thread.currentThread().getName() + "]创建线程[" + thread.getName() + "]"); | ||
+ | System.out.println("main 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | ===线程优先级常量=开始====== | ||
+ | 线程优先级MIN_PRIORITY=1 | ||
+ | 线程优先级NORM_PRIORITY=5 | ||
+ | 线程优先级MAX_PRIORITY=10 | ||
+ | ===线程优先级常量=结束====== | ||
+ | main 开始 | ||
+ | 线程[main]优先级[默认][5] | ||
+ | ========= | ||
+ | 线程[main]优先级[设置][11] | ||
+ | 设置[11]失败! | ||
+ | 线程优先级设置范围在[1]~[10] | ||
+ | ========= | ||
+ | 线程[main]优先级[设置][8] | ||
+ | 线程[main]创建线程[线程A] | ||
+ | main 结束 | ||
+ | run 开始 | ||
+ | 线程[线程A]优先级[继承][8] | ||
+ | run 结束 | ||
+ | </code> | ||
+ | * 优先级的规则性:优先级高的线程大部分先执行完 | ||
+ | * 优先级的随机性:优先级高的线程不是所有都先执行完 | ||
+ | * 优先级的速度:优先级高的线程运行速度快 | ||
+ | <code java MyThread.java> | ||
+ | package com.gxx.threads.study.test13; | ||
+ | |||
+ | import java.util.Date; | ||
+ | import java.util.Random; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 构造方法 | ||
+ | * @param name 线程名字 | ||
+ | * @param priority 优先级 | ||
+ | */ | ||
+ | public MyThread(String name, int priority) { | ||
+ | this.setName(name);//设置线程名字 | ||
+ | this.setPriority(priority);//设置优先级 | ||
+ | } | ||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | long startTime = new Date().getTime(); | ||
+ | for(int i=0; i<10000000; i++){ | ||
+ | new Random().nextInt(); | ||
+ | } | ||
+ | long endTime = new Date().getTime(); | ||
+ | String name = "[高优先级]"; | ||
+ | if(this.getPriority() <= 5){ | ||
+ | name = "[低优先级]"; | ||
+ | } | ||
+ | System.out.println(name + "线程[" + Thread.currentThread().getName() + "]运行结束,耗时[" + (endTime - startTime) + "]毫秒~"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test13; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试线程优先级的规则性,随机性,速度 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println("main 开始"); | ||
+ | MyThread thread1 = new MyThread("线程1", 10); | ||
+ | MyThread thread2 = new MyThread("线程2", 1); | ||
+ | MyThread thread3 = new MyThread("线程3", 10); | ||
+ | MyThread thread4 = new MyThread("线程4", 1); | ||
+ | MyThread thread5 = new MyThread("线程5", 10); | ||
+ | MyThread thread6 = new MyThread("线程6", 1); | ||
+ | MyThread thread7 = new MyThread("线程7", 10); | ||
+ | MyThread thread8 = new MyThread("线程8", 1); | ||
+ | MyThread thread9 = new MyThread("线程9", 10); | ||
+ | MyThread thread10 = new MyThread("线程10", 1); | ||
+ | |||
+ | thread1.start(); | ||
+ | thread2.start(); | ||
+ | thread3.start(); | ||
+ | thread4.start(); | ||
+ | thread5.start(); | ||
+ | thread6.start(); | ||
+ | thread7.start(); | ||
+ | thread8.start(); | ||
+ | thread9.start(); | ||
+ | thread10.start(); | ||
+ | System.out.println("main 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | main 开始 | ||
+ | main 结束 | ||
+ | [高优先级]线程[线程1]运行结束,耗时[8021]毫秒~ | ||
+ | [高优先级]线程[线程9]运行结束,耗时[8135]毫秒~ | ||
+ | [低优先级]线程[线程6]运行结束,耗时[8168]毫秒~ | ||
+ | [高优先级]线程[线程3]运行结束,耗时[8167]毫秒~ | ||
+ | [低优先级]线程[线程2]运行结束,耗时[8167]毫秒~ | ||
+ | [高优先级]线程[线程7]运行结束,耗时[8174]毫秒~ | ||
+ | [低优先级]线程[线程10]运行结束,耗时[8185]毫秒~ | ||
+ | [低优先级]线程[线程8]运行结束,耗时[8195]毫秒~ | ||
+ | [低优先级]线程[线程4]运行结束,耗时[8198]毫秒~ | ||
+ | [高优先级]线程[线程5]运行结束,耗时[8205]毫秒~ | ||
+ | </code> | ||
+ | ==== 守护线程 ==== | ||
+ | 守护线程比如垃圾回收器GC,当没有非守护线程的时候,守护线程没有意义,会自动停止! | ||
+ | <code java MyThread.java> | ||
+ | package com.gxx.threads.study.test14; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 计数 | ||
+ | */ | ||
+ | int count = 0; | ||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | while(true){ | ||
+ | try { | ||
+ | Thread.sleep(1000); | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | System.out.println("[" + this.getName() + "][守护线程]打印count=" + (++count)); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test14; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试守护线程 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println("[main][非守护线程] 开始"); | ||
+ | MyThread thread = new MyThread(); | ||
+ | thread.setName("Thead"); | ||
+ | System.out.println("[线程][" + thread.getName() + "]默认为" + (thread.isDaemon()?"[true][守护线程]":"[false][非守护线程]")); | ||
+ | System.out.println("[线程][" + thread.getName() + "]设置为[true][守护线程]"); | ||
+ | thread.setDaemon(true); | ||
+ | thread.start(); | ||
+ | try { | ||
+ | Thread.sleep(10000); | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | System.out.println("[main][非守护线程] 结束,则[守护线程]也自动结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | [main][非守护线程] 开始 | ||
+ | [线程][Thead]默认为[false][非守护线程] | ||
+ | [线程][Thead]设置为[true][守护线程] | ||
+ | [Thead][守护线程]打印count=1 | ||
+ | [Thead][守护线程]打印count=2 | ||
+ | [Thead][守护线程]打印count=3 | ||
+ | [Thead][守护线程]打印count=4 | ||
+ | [Thead][守护线程]打印count=5 | ||
+ | [Thead][守护线程]打印count=6 | ||
+ | [Thead][守护线程]打印count=7 | ||
+ | [Thead][守护线程]打印count=8 | ||
+ | [Thead][守护线程]打印count=9 | ||
+ | [main][非守护线程] 结束,则[守护线程]也自动结束 | ||
+ | </code> | ||
+ | ===== 对象及变量的并发访问 ===== | ||
+ | ==== synchronized同步方法 ==== | ||
+ | === 方法内变量为线程安全 === | ||
+ | <code java NumProcess.java> | ||
+ | package com.gxx.threads.study.test15; | ||
+ | |||
+ | /** | ||
+ | * 数字处理器 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class NumProcess { | ||
+ | /** | ||
+ | * 处理逻辑 | ||
+ | */ | ||
+ | public void process(){ | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]进入process~"); | ||
+ | /** | ||
+ | * 方法内变量 | ||
+ | */ | ||
+ | int num; | ||
+ | /** | ||
+ | * 根据线程名称走不同分支 | ||
+ | */ | ||
+ | if(Thread.currentThread().getName().equals("线程A")){ | ||
+ | num = 100; | ||
+ | try { | ||
+ | Thread.sleep(2000);//睡眠2秒 | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | } else { | ||
+ | num = 200; | ||
+ | } | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "] num=" + num); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java MyThead.java> | ||
+ | package com.gxx.threads.study.test15; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 数字处理器 | ||
+ | */ | ||
+ | NumProcess numProcess; | ||
+ | |||
+ | /** | ||
+ | * 构造方法 | ||
+ | * @param name | ||
+ | * @param numProcess | ||
+ | */ | ||
+ | public MyThread(String name, NumProcess numProcess) { | ||
+ | this.setName(name); | ||
+ | this.numProcess = numProcess; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | numProcess.process(); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test15; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试方法内变量并发访问 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println("main 开始"); | ||
+ | NumProcess numProcess = new NumProcess(); | ||
+ | MyThread threadA = new MyThread("线程A", numProcess); | ||
+ | MyThread threadB = new MyThread("线程B", numProcess); | ||
+ | threadA.start(); | ||
+ | threadB.start(); | ||
+ | System.out.println("main 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | main 开始 | ||
+ | main 结束 | ||
+ | [线程A]进入process~ | ||
+ | [线程B]进入process~ | ||
+ | [线程B] num=200 | ||
+ | [线程A] num=100 | ||
+ | </code> | ||
+ | === 实例变量非线程安全 === | ||
+ | <code java NumProcess.java> | ||
+ | package com.gxx.threads.study.test16; | ||
+ | |||
+ | /** | ||
+ | * 数字处理器 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class NumProcess { | ||
+ | /** | ||
+ | * 实例变量 | ||
+ | */ | ||
+ | int num; | ||
+ | /** | ||
+ | * 处理逻辑 | ||
+ | */ | ||
+ | public void process(){ | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]进入process~"); | ||
+ | /** | ||
+ | * 根据线程名称走不同分支 | ||
+ | */ | ||
+ | if(Thread.currentThread().getName().equals("线程A")){ | ||
+ | num = 100; | ||
+ | try { | ||
+ | Thread.sleep(2000);//睡眠2秒 | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | } else { | ||
+ | num = 200; | ||
+ | } | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "] num=" + num); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java MyThead.java> | ||
+ | package com.gxx.threads.study.test16; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 数字处理器 | ||
+ | */ | ||
+ | NumProcess numProcess; | ||
+ | |||
+ | /** | ||
+ | * 构造方法 | ||
+ | * @param name | ||
+ | * @param numProcess | ||
+ | */ | ||
+ | public MyThread(String name, NumProcess numProcess) { | ||
+ | this.setName(name); | ||
+ | this.numProcess = numProcess; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | numProcess.process(); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test16; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试实例变量并发访问 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println("main 开始"); | ||
+ | NumProcess numProcess = new NumProcess(); | ||
+ | MyThread threadA = new MyThread("线程A", numProcess); | ||
+ | MyThread threadB = new MyThread("线程B", numProcess); | ||
+ | threadA.start(); | ||
+ | threadB.start(); | ||
+ | System.out.println("main 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | main 开始 | ||
+ | main 结束 | ||
+ | [线程A]进入process~ | ||
+ | [线程B]进入process~ | ||
+ | [线程B] num=200 | ||
+ | [线程A] num=200 | ||
+ | </code> | ||
+ | === 实例变量非线程安全,synchronized同步 === | ||
+ | <code java NumProcess.java> | ||
+ | package com.gxx.threads.study.test17; | ||
+ | |||
+ | /** | ||
+ | * 数字处理器 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class NumProcess { | ||
+ | /** | ||
+ | * 实例变量 | ||
+ | */ | ||
+ | int num; | ||
+ | /** | ||
+ | * 处理逻辑 | ||
+ | */ | ||
+ | public synchronized void process(){ | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]进入process~"); | ||
+ | /** | ||
+ | * 根据线程名称走不同分支 | ||
+ | */ | ||
+ | if(Thread.currentThread().getName().equals("线程A")){ | ||
+ | num = 100; | ||
+ | try { | ||
+ | Thread.sleep(2000);//睡眠2秒 | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | } else { | ||
+ | num = 200; | ||
+ | } | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "] num=" + num); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java MyThead.java> | ||
+ | package com.gxx.threads.study.test17; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 数字处理器 | ||
+ | */ | ||
+ | NumProcess numProcess; | ||
+ | |||
+ | /** | ||
+ | * 构造方法 | ||
+ | * @param name | ||
+ | * @param numProcess | ||
+ | */ | ||
+ | public MyThread(String name, NumProcess numProcess) { | ||
+ | this.setName(name); | ||
+ | this.numProcess = numProcess; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | numProcess.process(); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test17; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试实例变量并发访问,synchronized同步 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println("main 开始"); | ||
+ | NumProcess numProcess = new NumProcess(); | ||
+ | MyThread threadA = new MyThread("线程A", numProcess); | ||
+ | MyThread threadB = new MyThread("线程B", numProcess); | ||
+ | threadA.start(); | ||
+ | threadB.start(); | ||
+ | System.out.println("main 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | main 开始 | ||
+ | main 结束 | ||
+ | [线程A]进入process~ | ||
+ | [线程A] num=100 | ||
+ | [线程B]进入process~ | ||
+ | [线程B] num=200 | ||
+ | </code> | ||
+ | === 多个对象多个锁 === | ||
+ | <code java NumProcess.java> | ||
+ | package com.gxx.threads.study.test18; | ||
+ | |||
+ | /** | ||
+ | * 数字处理器 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class NumProcess { | ||
+ | /** | ||
+ | * 实例变量 | ||
+ | */ | ||
+ | int num; | ||
+ | /** | ||
+ | * 处理逻辑 | ||
+ | */ | ||
+ | public synchronized void process(){ | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]进入process~"); | ||
+ | /** | ||
+ | * 根据线程名称走不同分支 | ||
+ | */ | ||
+ | if(Thread.currentThread().getName().equals("线程A")){ | ||
+ | num = 100; | ||
+ | try { | ||
+ | Thread.sleep(2000);//睡眠2秒 | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | } else { | ||
+ | num = 200; | ||
+ | } | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "] num=" + num); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java MyThead.java> | ||
+ | package com.gxx.threads.study.test18; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 数字处理器 | ||
+ | */ | ||
+ | NumProcess numProcess; | ||
+ | |||
+ | /** | ||
+ | * 构造方法 | ||
+ | * @param name | ||
+ | * @param numProcess | ||
+ | */ | ||
+ | public MyThread(String name, NumProcess numProcess) { | ||
+ | this.setName(name); | ||
+ | this.numProcess = numProcess; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | numProcess.process(); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test18; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试多个对象多个锁 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println("main 开始"); | ||
+ | NumProcess numProcess1 = new NumProcess(); | ||
+ | MyThread threadA = new MyThread("线程A", numProcess1); | ||
+ | NumProcess numProcess2 = new NumProcess(); | ||
+ | MyThread threadB = new MyThread("线程B", numProcess2); | ||
+ | threadA.start(); | ||
+ | threadB.start(); | ||
+ | System.out.println("main 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | main 开始 | ||
+ | main 结束 | ||
+ | [线程A]进入process~ | ||
+ | [线程B]进入process~ | ||
+ | [线程B] num=200 | ||
+ | [线程A] num=100 | ||
+ | </code> | ||
+ | === synchronized方法与锁对象 === | ||
+ | * 线程进synchronized方法获取锁,都是锁对象实例,其他线程可以进该实例其他不带synchronized的方法 | ||
+ | <code java MethodProcess.java> | ||
+ | package com.gxx.threads.study.test19; | ||
+ | |||
+ | /** | ||
+ | * 方法处理器 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MethodProcess { | ||
+ | /** | ||
+ | * 方法A | ||
+ | */ | ||
+ | public synchronized void methodA(){ | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]进入methodA~"); | ||
+ | try { | ||
+ | Thread.sleep(2000); | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]methodA结束"); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * 方法B | ||
+ | */ | ||
+ | public void methodB(){ | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]进入methodB~"); | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]methodB结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java MyThead.java> | ||
+ | package com.gxx.threads.study.test19; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 方法处理器 | ||
+ | */ | ||
+ | MethodProcess methodProcess; | ||
+ | |||
+ | /** | ||
+ | * 构造方法 | ||
+ | * @param name | ||
+ | * @param methodProcess | ||
+ | */ | ||
+ | public MyThread(String name, MethodProcess methodProcess) { | ||
+ | this.setName(name); | ||
+ | this.methodProcess = methodProcess; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | if("线程A".equals(this.getName())){ | ||
+ | methodProcess.methodA(); | ||
+ | } else { | ||
+ | methodProcess.methodB(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test19; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试synchronized方法与锁对象 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println("main 开始"); | ||
+ | MethodProcess methodProcess = new MethodProcess(); | ||
+ | MyThread threadA = new MyThread("线程A", methodProcess); | ||
+ | MyThread threadB = new MyThread("线程B", methodProcess); | ||
+ | threadA.start(); | ||
+ | threadB.start(); | ||
+ | System.out.println("main 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | main 开始 | ||
+ | main 结束 | ||
+ | [线程A]进入methodA~ | ||
+ | [线程B]进入methodB~ | ||
+ | [线程B]methodB结束 | ||
+ | [线程A]methodA结束 | ||
+ | </code> | ||
+ | * 线程A进synchronized的方法A,获取了锁,同时,线程B想进synchronized的方法B,得排队 | ||
+ | <code java MethodProcess.java> | ||
+ | package com.gxx.threads.study.test20; | ||
+ | |||
+ | /** | ||
+ | * 方法处理器 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MethodProcess { | ||
+ | /** | ||
+ | * 方法A | ||
+ | */ | ||
+ | public synchronized void methodA(){ | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]进入methodA~"); | ||
+ | try { | ||
+ | Thread.sleep(2000); | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]methodA结束"); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * 方法B | ||
+ | */ | ||
+ | public synchronized void methodB(){ | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]进入methodB~"); | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]methodB结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java MyThead.java> | ||
+ | package com.gxx.threads.study.test20; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 方法处理器 | ||
+ | */ | ||
+ | MethodProcess methodProcess; | ||
+ | |||
+ | /** | ||
+ | * 构造方法 | ||
+ | * @param name | ||
+ | * @param methodProcess | ||
+ | */ | ||
+ | public MyThread(String name, MethodProcess methodProcess) { | ||
+ | this.setName(name); | ||
+ | this.methodProcess = methodProcess; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | if("线程A".equals(this.getName())){ | ||
+ | methodProcess.methodA(); | ||
+ | } else { | ||
+ | methodProcess.methodB(); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test20; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试synchronized方法与锁对象 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println("main 开始"); | ||
+ | MethodProcess methodProcess = new MethodProcess(); | ||
+ | MyThread threadA = new MyThread("线程A", methodProcess); | ||
+ | MyThread threadB = new MyThread("线程B", methodProcess); | ||
+ | threadA.start(); | ||
+ | threadB.start(); | ||
+ | System.out.println("main 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | main 开始 | ||
+ | main 结束 | ||
+ | [线程A]进入methodA~ | ||
+ | [线程A]methodA结束 | ||
+ | [线程B]进入methodB~ | ||
+ | [线程B]methodB结束 | ||
+ | </code> | ||
+ | === synchronized锁重入与异常释放锁 === | ||
+ | * 当前类中不同synchronized方法可以锁重入 | ||
+ | * 父子类中不同synchronized方法可以锁重入 | ||
+ | * 发生异常,立刻释放锁 | ||
+ | <code java MethodProcess.java> | ||
+ | package com.gxx.threads.study.test21; | ||
+ | |||
+ | /** | ||
+ | * 方法处理器 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MethodProcess extends MethodProcessParent { | ||
+ | /** | ||
+ | * 方法A | ||
+ | */ | ||
+ | public synchronized void methodA(){ | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]进入methodA~"); | ||
+ | methodB(); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * 方法B | ||
+ | */ | ||
+ | public synchronized void methodB(){ | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]进入methodB~"); | ||
+ | methodC(); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * 方法C | ||
+ | */ | ||
+ | public synchronized void methodC(){ | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]进入methodC~"); | ||
+ | super.methodD(); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java MethodProcessParent.java> | ||
+ | package com.gxx.threads.study.test21; | ||
+ | |||
+ | /** | ||
+ | * 方法处理器 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MethodProcessParent { | ||
+ | /** | ||
+ | * 计数 | ||
+ | */ | ||
+ | int count = 0; | ||
+ | |||
+ | /** | ||
+ | * 方法D | ||
+ | */ | ||
+ | public synchronized void methodD(){ | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]进入methodD~"); | ||
+ | while (true) { | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]打印count=" + count); | ||
+ | try { | ||
+ | Thread.sleep(1000); | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | if(++count == 5){ | ||
+ | count = 0; | ||
+ | throw new RuntimeException("手动抛出异常~"); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java MyThead.java> | ||
+ | package com.gxx.threads.study.test21; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 方法处理器 | ||
+ | */ | ||
+ | MethodProcess methodProcess; | ||
+ | |||
+ | /** | ||
+ | * 构造方法 | ||
+ | * @param name | ||
+ | * @param methodProcess | ||
+ | */ | ||
+ | public MyThread(String name, MethodProcess methodProcess) { | ||
+ | this.setName(name); | ||
+ | this.methodProcess = methodProcess; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | methodProcess.methodA(); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test21; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试synchronized锁重入 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println("main 开始"); | ||
+ | MethodProcess methodProcess = new MethodProcess(); | ||
+ | MyThread threadA = new MyThread("线程A", methodProcess); | ||
+ | threadA.start(); | ||
+ | MyThread threadB = new MyThread("线程B", methodProcess); | ||
+ | threadB.start(); | ||
+ | System.out.println("main 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | main 开始 | ||
+ | main 结束 | ||
+ | [线程A]进入methodA~ | ||
+ | [线程A]进入methodB~ | ||
+ | [线程A]进入methodC~ | ||
+ | [线程A]进入methodD~ | ||
+ | [线程A]打印count=0 | ||
+ | [线程A]打印count=1 | ||
+ | [线程A]打印count=2 | ||
+ | [线程A]打印count=3 | ||
+ | [线程A]打印count=4 | ||
+ | [线程B]进入methodA~ | ||
+ | Exception in thread "线程A" java.lang.RuntimeException: 手动抛出异常~ | ||
+ | at com.gxx.threads.study.test21.MethodProcessParent.methodD(MethodProcessParent.java:27) | ||
+ | at com.gxx.threads.study.test21.MethodProcess.methodC(MethodProcess.java:29) | ||
+ | at com.gxx.threads.study.test21.MethodProcess.methodB(MethodProcess.java:21) | ||
+ | at com.gxx.threads.study.test21.MethodProcess.methodA(MethodProcess.java:13) | ||
+ | at com.gxx.threads.study.test21.MyThread.run(MyThread.java:28) | ||
+ | [线程B]进入methodB~ | ||
+ | [线程B]进入methodC~ | ||
+ | [线程B]进入methodD~ | ||
+ | [线程B]打印count=0 | ||
+ | [线程B]打印count=1 | ||
+ | [线程B]打印count=2 | ||
+ | [线程B]打印count=3 | ||
+ | [线程B]打印count=4 | ||
+ | Exception in thread "线程B" java.lang.RuntimeException: 手动抛出异常~ | ||
+ | at com.gxx.threads.study.test21.MethodProcessParent.methodD(MethodProcessParent.java:27) | ||
+ | at com.gxx.threads.study.test21.MethodProcess.methodC(MethodProcess.java:29) | ||
+ | at com.gxx.threads.study.test21.MethodProcess.methodB(MethodProcess.java:21) | ||
+ | at com.gxx.threads.study.test21.MethodProcess.methodA(MethodProcess.java:13) | ||
+ | at com.gxx.threads.study.test21.MyThread.run(MyThread.java:28) | ||
+ | </code> | ||
+ | === 同步不具有继承性 === | ||
+ | <code java MethodProcess.java> | ||
+ | package com.gxx.threads.study.test22; | ||
+ | |||
+ | /** | ||
+ | * 方法处理器 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MethodProcess extends MethodProcessParent { | ||
+ | /** | ||
+ | * 处理 | ||
+ | */ | ||
+ | public void process(){ | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]进入process~"); | ||
+ | try { | ||
+ | Thread.sleep(2000); | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]process结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java MethodProcessParent.java> | ||
+ | package com.gxx.threads.study.test22; | ||
+ | |||
+ | /** | ||
+ | * 方法处理器 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MethodProcessParent { | ||
+ | /** | ||
+ | * 处理 | ||
+ | */ | ||
+ | public synchronized void process(){ | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]进入process~"); | ||
+ | try { | ||
+ | Thread.sleep(2000); | ||
+ | } catch (InterruptedException e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | System.out.println("[" + Thread.currentThread().getName() + "]process结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java MyThead.java> | ||
+ | package com.gxx.threads.study.test22; | ||
+ | |||
+ | /** | ||
+ | * 自定义线程类 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class MyThread extends Thread { | ||
+ | /** | ||
+ | * 方法处理器 | ||
+ | */ | ||
+ | MethodProcess methodProcess; | ||
+ | |||
+ | /** | ||
+ | * 构造方法 | ||
+ | * @param name | ||
+ | * @param methodProcess | ||
+ | */ | ||
+ | public MyThread(String name, MethodProcess methodProcess) { | ||
+ | this.setName(name); | ||
+ | this.methodProcess = methodProcess; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * 覆盖run方法 | ||
+ | */ | ||
+ | @Override | ||
+ | public void run() { | ||
+ | methodProcess.process(); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | <code java Test.java> | ||
+ | package com.gxx.threads.study.test22; | ||
+ | |||
+ | /** | ||
+ | * 测试类-测试同步不具有继承性 | ||
+ | * @author Gxx | ||
+ | */ | ||
+ | public class Test { | ||
+ | /** | ||
+ | * main方法 | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) { | ||
+ | System.out.println("main 开始"); | ||
+ | MethodProcess methodProcess = new MethodProcess(); | ||
+ | MyThread threadA = new MyThread("线程A", methodProcess); | ||
+ | MyThread threadB = new MyThread("线程B", methodProcess); | ||
+ | threadA.start(); | ||
+ | threadB.start(); | ||
+ | System.out.println("main 结束"); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | 输出 | ||
+ | <code> | ||
+ | main 开始 | ||
+ | main 结束 | ||
+ | [线程B]进入process~ | ||
+ | [线程A]进入process~ | ||
+ | [线程B]process结束 | ||
+ | [线程A]process结束 | ||
+ | </code> |