多線程編程

系统管理员 2022-05-22 12:34 335阅读 0赞

原文:http://www.weixueyuan.net/view/6027.html

一、線程的概念

基于进程(process)的多任务处理是更熟悉的形式。进程本质上是一个执行的程序。因此,基于进程(process-based) 的多任务处理的特点是允许你的计算机同时运行两个或更多的程序。举例来说,基于进程的多任务处理使你在运用文本编辑器的时候可以同时运行Java编译器。在基于进程的多任务处理中,程序是调度程序所分派的最小代码单位。

在基于线程(thread-based)的多任务处理环境中,线程是最小的执行单位。这意味着一个程序可以同时执行两个或者多个任务的功能。例如,一个文本编辑器可以在打印的同时格式化文本。所以,多进程程序处理“大图片”,而多线程程序处理细节问题。

多线程程序比多进程程序需要更少的管理费用。进程是重量级的任务,需要分配它们自己独立的地址空间。进程间通信是昂贵和受限的。进程间的转换也是很需要花费的。另一方面,线程是轻量级的选手。它们共享相同的地址空间并且共同分享同一个进程。线程间通信是便宜的,线程间的转换也是低成本的。当Java程序使用多进程任务处理环境时,多进程程序不受Java的控制,而多线程则受Java控制。

多线程帮助你写出CPU最大利用率的高效程序,因为空闲时间保持最低。

二、線程模型

单线程系统的处理途径是使用一种叫作轮询的事件循环方法。在该模型中,单线程控制在一无限循环中运行,轮询一个事件序列来决定下一步做什么。一旦轮询装置返回信号表明,已准备好读取网络文件,事件循环调度控制管理到适当的事件处理程序。直到事件处理程序返回,系统中没有其他事件发生。这就浪费了CPU时间。这导致了程序的一部分独占了系统,阻止了其他事件的执行。总的来说,单线程环境,当一个线程因为等待资源时阻塞(block,挂起执行),整个程序停止运行。

Java多线程的优点在于取消了主循环/轮询机制。一个线程可以暂停而不影响程序的其他部分。例如,当一个线程从网络读取数据或等待用户输入时产生的空闲时间可以被利用到其他地方。多线程允许活的循环在每一帧间隙中沉睡一秒而不暂停整个系统。在Java程序中出现线程阻塞,仅有一个线程暂停,其他线程继续运行。

线程存在于好几种状态。线程可以正在运行(running)。只要获得CPU时间它就可以运行。运行的线程可以被挂起(suspend),并临时中断它的执行。一个挂起的线程可以被恢复(resume,允许它从停止的地方继续运行。一个线程可以在等待资源时被阻塞(block)。

在任何时候,线程可以终止(terminate),这立即中断了它的运行。一旦终止,线程不能被恢复。

Thread 类和Runnable 接口

Thread类封装了线程的执行。既然你不能直接引用运行着的线程的状态,你要通过它的代理处理它,于是Thread 实例产生了。为创建一个新的线程,你的程序必须扩展Thread 或实现Runnable接口。

常用的管理線程的方法




































方法 意義
getName 獲得線程名稱
getPriority 獲得線程優先級
IsAlive 判斷線程是否仍在運行
join 等待一個線程終止
run 線程的入口點
sleep 在第一段時間內挂起線程
start 通過調用運行方法來啟動線程

三、主線程

当Java程序启动时,一个线程立刻运行,该线程通常叫做程序的主线程(main thread),因为它是程序开始时就执行的。主线程的重要性体现在两方面:

  • 它是产生其他子线程的线程;
  • 通常它必须最后完成执行,因为它执行各种关闭动作。

尽管主线程在程序启动时自动创建,但它可以由一个Thread对象控制。为此,你必须调用方法currentThread()获得主線程的一个引用,currentThread()是Thread类的公有的静态成员。

  1. public class CurrentThreadDemo {
  2. public static void main(String[] args){
  3. Thread t=Thread.currentThread(); //獲取主線程引用
  4. System.out.println("Current thread:"+t);
  5. t.setName("My Thread"); //改變線程的名稱
  6. System.out.println("After name change:"+t);
  7. try{
  8. for(int n=5;n>0;n--){
  9. System.out.println(n);
  10. Thread.sleep(1000); //線程挂起,每數一次暫停一秒
  11. }
  12. }catch (InterruptedException e){ //其他線程想要打攪沉睡的主線程時
  13. System.out.println("Main thread interrupted");
  14. }
  15. }
  16. }

運行結果:
Current thread:Thread[main,5,main]
After name change:Thread[My Thread,5,main]
5
4
3
2

1

默认情况下,主线程的名称是main。它的优先级是5,这也是默认值,main也是所属线程组的名称。一个线程组(thread group)是一种将线程作为一个整体集合的状态控制的数据结构。

四、創建線程(Runnable接口和Thread類)

大多数情况,通过实例化一个Thread对象来创建一个线程。Java定义了两种方式:

  • 实现Runnable 接口;
  • 可以继承Thread类。

實現Runnable接口

  1. /**創建一個新線程並啟動它運行**/
  2. public class ThreadDemo {
  3. public static void main(String[] args){
  4. new NewThread(); //創建新線程
  5. try{
  6. for(int i=5;i>0;i--){
  7. System.out.println("Main Thread:"+i);
  8. Thread.sleep(1000);
  9. }
  10. }catch (InterruptedException e){
  11. System.out.println("Main thread interrupted.");
  12. }
  13. System.out.println("Main thread exiting");
  14. }
  15. }
  16. class NewThread implements Runnable{
  17. Thread t;
  18. NewThread(){
  19. //創建一個新的子線程
  20. /*public Thread​(Runnable target,String name)
  21. target - 指定啟動線程時,調用哪個runnable實例對象的run方法,
  22. 若為null,調用該thread實例對象的run方法;
  23. name - 線程的名稱
  24. */
  25. t=new Thread(this,"Demo Thread");
  26. System.out.println("Child thread:"+t);
  27. t.start(); //運行線程(實際上JVM調用run())
  28. }
  29. //線程的入口
  30. public void run(){
  31. try{
  32. for(int i=5;i>0;i--){
  33. System.out.println("Child Thread:"+i);
  34. Thread.sleep(500); //子線程挂起(sleep是一個靜態函數)
  35. }
  36. }catch (InterruptedException e){
  37. System.out.println("Child interrupted.");
  38. }
  39. System.out.println("Exiting child thread.");
  40. }
  41. }

運行結果:
Child thread:Thread[Demo Thread,5,main]
Main Thread:5
Child Thread:5
Child Thread:4
Main Thread:4
Child Thread:3
Child Thread:2
Main Thread:3
Child Thread:1
Exiting child thread.
Main Thread:2
Main Thread:1

Main thread exiting

注意:在多线程程序中,通常主线程必须是结束运行的最后一个线程,且程序剛開始的時候(無sleep()執行導致線程挂起等),main線程的優先級最高。实际上,一些老的JVM,如果主线程先于子线程结束,Java的运行时间系统就可能“挂起”。前述程序保证了主线程最后结束,因为主线程沉睡周期1000毫秒,而子线程仅为500毫秒。这就使子线程在主线程结束之前先结束。

擴展Thread

当一个类继承Thread时,它必须重载run()方法,这个run()方法是新线程的入口,它也必须调用start()方法去启动新线程执行。

  1. public class _ExtendThread {
  2. public static void main(String[] args){
  3. new _NewThread();
  4. try{
  5. for(int i=5;i>0;i--){
  6. System.out.println("Main Thread:"+i);
  7. Thread.sleep(1000);
  8. }
  9. }catch (InterruptedException e){
  10. System.out.println("Main thread interrupted.");
  11. }
  12. System.out.println("Main thread exiting.");
  13. }
  14. }
  15. class _NewThread extends Thread{
  16. _NewThread(){
  17. super("Demo Thread"); //可注釋掉
  18. System.out.println("Child thread:"+this);
  19. start(); //啟動線程(調用run())
  20. }
  21. //子線程入口
  22. public void run(){
  23. try{
  24. for(int i=5;i>0;i--){
  25. System.out.println("Child Thread:"+i);
  26. Thread.sleep(500);
  27. }
  28. }catch (InterruptedException e){
  29. System.out.println("Child interrupted.");
  30. }
  31. System.out.println("Exiting child thread.");
  32. }
  33. }

運行結果:

(同上段代碼)

注意:很多Java程序员认为类仅在它们被加强或修改时应该被扩展。因此,如果你不重载Thread的其他方法时,最好只实现Runnable 接口。

五、創建多線程

  1. public class multiThreadDemo {
  2. public static void main(String args[]) {
  3. //創建三個子線程
  4. new __NewThread("One"); // start threads
  5. new __NewThread("Two");
  6. new __NewThread("Three");
  7. try {
  8. // wait for other threads to end
  9. Thread.sleep(10000);
  10. } catch (InterruptedException e) {
  11. System.out.println("Main thread Interrupted");
  12. }
  13. System.out.println("Main thread exiting.");
  14. }
  15. }
  16. //創建多線程
  17. // Create multiple threads.
  18. class __NewThread implements Runnable {
  19. String name; // name of thread
  20. Thread t;
  21. __NewThread(String threadname) {
  22. name = threadname;
  23. t = new Thread(this, name);
  24. System.out.println("New thread: " + t);
  25. t.start(); // Start the thread
  26. }
  27. // This is the entry point for thread.
  28. public void run() {
  29. try {
  30. for(int i = 5; i > 0; i--) {
  31. System.out.println(name + ": " + i);
  32. Thread.sleep(1000);
  33. }
  34. } catch (InterruptedException e) {
  35. System.out.println(name + "Interrupted");
  36. }
  37. System.out.println(name + " exiting.");
  38. }
  39. }

運行結果:
New thread: Thread[One,5,main]
New thread: Thread[Two,5,main]
New thread: Thread[Three,5,main]
Three: 5
Two: 5
One: 5
Two: 4
Three: 4
One: 4
Two: 3
One: 3
Three: 3
One: 2
Two: 2
Three: 2
Three: 1
Two: 1
One: 1
Three exiting.
One exiting.
Two exiting.

Main thread exiting.

注意:一旦启动,所有三个子线程共享CPU。注意main()中对sleep(10000)的调用。这使主线程沉睡十秒确保它最后结束。

六、isAlive()和join()的使用

  • public final boolean isAlive():檢查線程是否結束;如果線程被生成了,但還未被啟動,isAlive()將返回false,此時調用join()無效,將直接繼續往下執行
  • public final void join() throws InterruptedException:當前線程等待子線程的終止

    public class demoJoin {

    1. public static void main(String[] args){
    2. NewThread ob1=new NewThread("One");
    3. NewThread ob2=new NewThread("two");
    4. NewThread ob3=new NewThread("Three");
    5. //判斷線程是否存在
    6. System.out.println("Thread One is alive:"+ob1.t.isAlive());
    7. System.out.println("Thread Two is alive:"+ob2.t.isAlive());
    8. System.out.println("Thread Three is alive:"+ob3.t.isAlive());
    9. //等待線程結束
    10. try{
    11. System.out.println("Waiting for threads to finish.");
    12. ob1.t.join();
    13. ob2.t.join();
    14. ob3.t.join();
    15. }catch (InterruptedException e){
    16. System.out.println("Main thread Interrupted.");
    17. }
    18. System.out.println("Thread One is alive:"+ob1.t.isAlive());
    19. System.out.println("Thread Two is alive:"+ob2.t.isAlive());
    20. System.out.println("Thread Three is alive:"+ob3.t.isAlive());
    21. System.out.println("Main thread exiting.");
    22. }

    }

    class NewThread implements Runnable{

    1. String name; //線程名
    2. Thread t;
    3. NewThread(String threadname){
    4. name=threadname;
    5. t=new Thread(this,name);
    6. System.out.println("New thread:"+t);
    7. t.start(); //啟動線程
    8. }
    9. public void run(){
    10. try{
    11. for(int i=5;i>0;i--){
    12. System.out.println(name+":"+i);
    13. Thread.sleep(1000);
    14. }
    15. }catch (InterruptedException e){
    16. System.out.println(name+"interrupted.");
    17. }
    18. System.out.println(name+"exiting.");
    19. }

    }

運行結果:
New thread:Thread[One,5,main]
New thread:Thread[two,5,main]
New thread:Thread[Three,5,main]
Thread One is alive:true
Thread Two is alive:true
Thread Three is alive:true
Waiting for threads to finish.
One:5
Three:5
two:5
One:4
Three:4
two:4
One:3
Three:3
two:3
One:2
Three:2
two:2
One:1
two:1
Three:1
Oneexiting.
twoexiting.
Threeexiting.
Thread One is alive:false
Thread Two is alive:false
Thread Three is alive:false

Main thread exiting.

注意:調用join後返回,線程終止執行

七、線程優先級

八、線程同步

九、線程間通信

十、線程死鎖

十一、線程的掛起、恢復和終止

发表评论

表情:
评论列表 (有 0 条评论,335人围观)

还没有评论,来说两句吧...

相关阅读

    相关 C#組件(2)

    C\組件編程(2) 較复雜的屬性為了編輯方便,就要用到屬性編輯器. 由于在屬性視窗中只能識別字符串類型,如果是非字符串類型的屬性還需要用到類型轉換器. 下面就分別講它

    相关 Socket--同步的應用

    同步編程使用的情況不多,在以下3種情況下可以使用同步: 1.客戶端數量較少情況下的服務端編程 2.客戶端數量較多,但都是短連接情況下的服務端編程 3.客戶端編程

    相关 JS規範 積累

    1.命名推薦:userName(第一個單詞首字母小寫,其他首字母大寫) 2.函數名有必要加上前綴fn,比如fnGetName,最好採用動名詞組合,函數的參數要加註釋 3

    相关 的學習

      多線程在構建大型系統的時候是需要重點關注的一個重要方面,特別是在效率(系統跑得多快?)和性能(系統工作正常?)之間做一個權衡的時候。恰當的使用多線程可以極大的提高系統性能。