Java的多线程编程教程
1- 线程的操作原理
![](/uploads/tutorial/20160326/1-1603261I249401.png)
2- 线程入门实例
这个例子中我们需要两个类参与:
-
HelloMain是含有main方法的类,它是一个主线程。
-
HelloThread是一个继承自 Thread 类的类。它被创建并启用到主线程流内运行,并且将平行于主线程运行。
![](file://E:/data/youdao/qq24B39E2329F08D9F4C3146A571A415EF/86a590d5ae0a4a4e8953cfeef893db8a/age-provider.png)
package com.yiibai.tutorial.thread.hellothread; public class HelloMain { public static void main(String[] args) throws InterruptedException { int idx = 1; for (int i = 0; i < 2; i++) { System.out.println("Main thread running " + idx++); // Sleep 2101 miliseconds. Thread.sleep(2101); } HelloThread helloThread = new HelloThread(); // Run thread helloThread.start(); for (int i = 0; i < 3; i++) { System.out.println("Main thread running " + idx++); // Sleep 2101 miliseconds. Thread.sleep(2101); } System.out.println("==> Main thread stopped"); } }
HelloThread.java
package com.yiibai.tutorial.thread.hellothread; public class HelloThread extends Thread { // Code of method run() will be executed when // thread call start() @Override public void run() { int index = 1; for (int i = 0; i < 10; i++) { System.out.println(" - HelloThread running " + index++); try { // Sleep 1030 miliseconds. Thread.sleep(1030); } catch (InterruptedException e) { } } System.out.println(" - ==> HelloThread stopped"); } }
运行 HelloMain 类的结果如下:
![](/uploads/tutorial/20160326/1-1603261J051G0.png)
![](/uploads/tutorial/20160326/1-1603261J051G0.png)
![](file://E:/data/youdao/qq24B39E2329F08D9F4C3146A571A415EF/90a1d873b235468f932a72cdafdc95d7/age-provider.png)
3- Runnable接口
您也可以从一个类来实现Runnable接口来创建一个线程。见例子:
RunnableDemo.java
package com.yiibai.tutorial.thread.runnable; public class RunnableDemo implements Runnable { @Override public void run() { int idx = 1; for (int i = 0; i < 5; i++) { System.out.println("Hello from RunnableDemo " + idx++); // Sleep 2 second. try { Thread.sleep(2000); } catch (InterruptedException e) { } } } }
RunnableTest.java
package com.yiibai.tutorial.thread.runnable; public class RunnableTest { public static void main(String[] args) throws InterruptedException { System.out.println("Main thread running.."); // Create a thread from Runnable. Thread thread = new Thread(new RunnableDemo()); thread.start(); // Sleep 5 seconds. Thread.sleep(5000); System.out.println("Main thread stopped"); } }
运行 RunnableTest 类:
![](/uploads/tutorial/20160326/1-1603261J41Q39.png)
![](/uploads/tutorial/20160326/1-1603261J41Q39.png)
![](file://E:/data/youdao/qq24B39E2329F08D9F4C3146A571A415EF/371fc5b0dfd749dcbf84dd3812c33958/age-provider.png)
4- 守护线程
Java的线程划分为2种类型:正常线程和守护线程。两者不同的是其结束的方式。在程序中,正常的线程和守护线程各自并行运行。当一切正常线程完成,所有的守护线程也将被终止。注:使用 setDeamon(boolean) 建立一个线程,可以设置其是或不是守护线程。 值得注意的是,只能在当线程尚未运行时调用setDeamon(boolean)。这意味着,当线程已经运行,就不能再从非守护线程改变为守护进程,反之亦然。
当创建一个新的线程,它继承了其父线程守护功能。因此,当你从一个类的main方法创建线程,该线程自然就是非守护使线程。因此,如果在一个守护线程创建一个新的线程,默认情况下它也是守护线程。
Thread thread = new MyThread(); // marks this thread as a daemon thread // This method is only called when the thread is not a start. // In the case of start, it will be throws an exception. thread.setDeamon(true); // marks this thread as a none-daemon thread // This method is only called when the thread is not a start. // In the case of start, it will be throws an exception. thread.setDeamon(false);
为了便于理解,我们考虑下面的例子。下面的三个类已经介入实例:
![](/uploads/tutorial/20160326/1-1603261JI4D6.png)
![](/uploads/tutorial/20160326/1-1603261JI4D6.png)
![](file://E:/data/youdao/qq24B39E2329F08D9F4C3146A571A415EF/cdf289ba645c4ac6a783cc7bb1d19136/age-provider.png)
NoneDeamonThread.java
package com.yiibai.tutorial.thread.deamon; public class NoneDeamonThread extends Thread { @Override public void run() { int i = 0; // Loop 10 times. This thread will end. while (i < 10) { System.out.println(" - Hello from None Deamon Thread " + i++); try { // Sleep 1 second Thread.sleep(1000); } catch (InterruptedException e) { } } // None deamon thread ending. System.out.println("\n==> None Deamon Thread ending\n"); } }
DeamonThread.java
package com.yiibai.tutorial.thread.deamon; class DeamonThread extends Thread { @Override public void run() { int count = 0; // Infinite loop while (true) { System.out.println("+ Hello from Deamon Thread " + count++); try { // Sleep 2 second sleep(2000); } catch (InterruptedException e) { } } } }
DaemonTest.java
package com.yiibai.tutorial.thread.deamon; public class DaemonTest { public static void main(String[] args) { System.out.println("==> Main Thread running..\n"); // Create thread Thread deamonThread = new DeamonThread(); // Set deamon true deamonThread.setDaemon(true); deamonThread.start(); // Create other thread new NoneDeamonThread().start(); try { // Sleep 5 second Thread.sleep(5000); } catch (InterruptedException e) { } // Main Thread ending System.out.println("\n==> Main Thread ending\n"); } }
运行类 DeamonTest:
![](/uploads/tutorial/20160326/1-1603261J6414I.png)
![](/uploads/tutorial/20160326/1-1603261J6414I.png)
![](file://E:/data/youdao/qq24B39E2329F08D9F4C3146A571A415EF/9a6ef4c5a5344ebeb2eb93201010bffd/age-provider.png)
上图显示,当一切正常的线程停止,虽然其代码是无限运行的,但是守护线程也已停止。
守护线程使用来做什么?
Java一个重要守护线程是垃圾收集线程。这意味着收集资源中不再使用解放出来的内存。当所有用户线程停止运行,垃圾收集线程也会停止。
5- 使用join() & join(long)
Thread.join()是一个方法通知父线程等待在继续运行之前要完成这个线程。
// Parent thread must wait until the end of this thread, before being continued. // (This is equivalent to calling join(0)) public final void join() throws InterruptedException; // Parent thread must wait 'millis' milliseconds to continue running. // After call join(long). // If the parameter millis = 0 means to wait until the end of this thread. public final synchronized void join(long millis) throws InterruptedException; // Parent thread must wait 'millis' milliseconds and 'nanos' nanoseconds to continue running. // After call join(long,int). // 1 second = 1000000 nanoseconds. public final synchronized void join(long millis, int nanos) throws InterruptedException;
考虑下面的一个例子:
![](/uploads/tutorial/20160326/1-1603261J925638.png)
![](/uploads/tutorial/20160326/1-1603261J925638.png)
![](file://E:/data/youdao/qq24B39E2329F08D9F4C3146A571A415EF/b7949a0d5bdc46c5b24ebad742ab71a8/age-provider.png)
JoinThread.java
package com.yiibai.tutorial.thread.join; public class JoinThread extends Thread { private String threadName; private int count; public JoinThread(String threadName, int count) { this.threadName = threadName; this.count = count; } @Override public void run() { for (int i = 1; i < count + 1; i++) { System.out.println("Hello from " + this.threadName + " " + i); try { Thread.sleep(2000); } catch (InterruptedException e) { } } System.out.println("\n==> Thread " + threadName + " end!\n"); } }
JoinTest.java
package com.yiibai.tutorial.thread.join; public class JoinTest { public static void main(String[] args) throws InterruptedException { System.out.println("\n==> Main thread starting..\n"); Thread joinThreadA = new JoinThread("A*", 2); Thread joinThreadB = new JoinThread("B*", 3); // None join Thread. Thread noJoinThreadC = new JoinThread("C", 5); joinThreadA.start(); joinThreadB.start(); noJoinThreadC.start(); // Using join() joinThreadA.join(); joinThreadB.join(); // The following code will have to wait until 2 // JoinThread A, B completed. System.out.println("Hello from main thread..."); System.out.println("Thread A isLive? " + joinThreadA.isAlive()); System.out.println("Thread B isLive? " + joinThreadB.isAlive()); System.out.println("Thread C isLive? " + noJoinThreadC.isAlive()); System.out.println("\n==> Main Thread end!\n"); } }
运行JoinTest的结果:
![](/uploads/tutorial/20160326/1-1603261K01XE.png)
![](/uploads/tutorial/20160326/1-1603261K01XE.png)
![](file://E:/data/youdao/qq24B39E2329F08D9F4C3146A571A415EF/feccf26328f34448a3add2a42783a1df/age-provider.png)
使用 join(long millis) 的例子:
JoinTest2.java
package com.yiibai.tutorial.thread.join; public class JoinTest2 { public static void main(String[] args) throws InterruptedException { System.out.println("\n==> Main thread starting..\n"); Thread joinThreadA = new JoinThread("A*", 5); joinThreadA.start(); // Main thread must wait to 5000 miliseconds, // and then continue running. (Not necessarily joinThreadA finish) joinThreadA.join(5000); System.out.println("Main thread after 5000 milli second"); System.out.println("Hello from main thread..."); System.out.println("Thread A isLive? " + joinThreadA.isAlive()); System.out.println("\n==> Main Thread end!\n"); } }
运行示例的结果:
![](/uploads/tutorial/20160326/1-1603261K4345V.png)
![](/uploads/tutorial/20160326/1-1603261K4345V.png)
![](file://E:/data/youdao/qq24B39E2329F08D9F4C3146A571A415EF/d1a1ad4baca749da92ba675118082297/age-provider.png)
6- 处理线程异常
Thread.setDefaultUncaughtExceptionHandler()方法设置默认处理程序,当一个线程突然终止由于未捕获到异常出现时调用该程序,并且没有其他的处理程序已经为该线程定义。
ThreadExceptionDemo.java
package com.yiibai.tutorial.thread.exception; import java.util.Random; public class ThreadExceptionDemo { public static class RunnableTest implements Runnable { @Override public void run() { System.out.println("Thread running .."); while (true) { Random r = new Random(); // A random number from 0-99 int i = r.nextInt(100); System.out.println("Next value " + i); try { Thread.sleep(2000); } catch (InterruptedException e) { } if (i > 70) { // Simulate an exception was not handled in the thread. throw new RuntimeException("Have a problem..."); } } } } public static void main(String[] args) { System.out.println("==> Main thread running..."); Thread thread = new Thread(new RunnableTest()); Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("#Thread: " + t); System.out.println("#Thread exception message: " + e.getMessage()); } }); thread.start(); System.out.println("==> Main thread end..."); } }
运行示例的结果:
![](/uploads/tutorial/20160326/1-1603261K300550.png)
![](/uploads/tutorial/20160326/1-1603261K300550.png)
![](file://E:/data/youdao/qq24B39E2329F08D9F4C3146A571A415EF/57d8d1bda4614106bc8fc855154caa86/age-provider.png)
7- 使用yield()
从理论上讲,“yield”是指放手,放弃,投降。让步线程告诉虚拟机,它的愿意让其他线程在它的位置进行调度。则表示它没有太关键事情做了。 请注意,这只是一个暗示,不能保证有任何效果。
yield()被定义为在 Thread.java 中如下:
public static native void yield();