1. 程式人生 > >Java執行緒:什麼是執行緒

Java執行緒:什麼是執行緒

一 基本概念

  多工:同一時刻執行多個程式的能力。每一個任務稱為一個執行緒。可以同時執行一個以上執行緒的程式稱為多執行緒程式。

  Java編寫程式都執行在在Java虛擬機器(JVM)中,在JVM的內部,程式的多工是通過執行緒來實現的。每用java命令啟動一個java應用程式,就會啟動一個JVM程序。在同一個JVM程序中,有且只有一個程序,就是它自己。在這個JVM環境中,所有程式程式碼的執行都是以執行緒來執行。

  一般常見的Java應用程式都是單執行緒的。比如,用java命令執行一個最簡單的HelloWorld的Java應用程式時,就啟動了一個JVM程序,JVM找到程式程式的入口點main(),然後執行main()方法,這樣就產生了一個執行緒,這個執行緒稱之為主執行緒。當main方法結束後,主執行緒執行完成。JVM程序也隨即退出 。

  對於一個程序中的多個執行緒來說,多個執行緒共享程序的記憶體塊,當有新的執行緒產生的時候,作業系統不分配新的記憶體,而是讓新執行緒共享原有的程序塊的記憶體。因此,執行緒間的通訊很容易,速度也很快。不同的程序因為處於不同的記憶體塊,因此程序之間的通訊相對困難。

  程序是指一個記憶體中執行的應用程式,每個程序都有自己獨立的一塊記憶體空間,一個程序中可以啟動多個執行緒。比如在Windows系統中,一個執行的exe就是一個程序。

  執行緒是指程序中的一個執行流程,一個程序可以執行多個執行緒。比如java.exe程序可以執行很多執行緒。執行緒總是輸入某個程序,程序中的多個執行緒共享程序的記憶體。

  Java中執行緒是指java.lang.Thread類的一個例項或執行緒的執行。使用java.lang.Thread或java.lang.Runnable介面編寫程式碼定義、例項化、啟動新執行緒。   Java中每個執行緒都有一個呼叫棧,即使不在程式中建立任何新的執行緒,執行緒也在後臺執行。main()方法執行在一個執行緒內,稱為主執行緒。一旦建立一個新的執行緒,就產生一個新的呼叫棧。   執行緒分為兩類:使用者執行緒和守候執行緒。當所有使用者執行緒執行完畢後,JVM自動關閉。但是守候執行緒卻不獨立與JVM,守候執行緒一般是有作業系統或使用者自己建立的。 二 定義執行緒 1 擴充套件java.lang.Thread類以及實現java.lang.Runnable介面。   此類中有run()方法,public void run(),如果該執行緒是獨立的Runnable執行物件構造的,則呼叫該Runnable物件的run()方法;否則,該方法不執行任何操作。Thread的子類也應該重寫該方法。 三 例項化執行緒
1 如果是擴充套件了java.lang.Thread類的執行緒,則直接呼叫new即可。 2 如果是實現了jav.lang.Runnable介面的類,則呼叫Thread的構造方法:   Thread(Runnable target)   Thread(Runnable target,String name)   Thread(ThreadGroup group, Runnable target)   Thread(ThreadGroup group, Runnable target, String name)   Thread(ThreadGroup group, Runnable target, String name, long stackSize) 四 啟動執行緒   線上程的Thread物件上呼叫start()方法,而不是run()或別的方法。   在呼叫start()方法之前,執行緒處於新狀態中,新狀態有一個Thread物件,但沒有一個真正的執行緒。   在呼叫start()方法之後,發生了一系列複雜的事情:       啟動新的執行執行緒(具有新的呼叫棧);     該執行緒從新狀態轉移到可執行狀態;     當該執行緒獲得執行機會時,其目標run()方法將執行。 五 注意事項   1 獲取當前執行緒的物件的方法是:Thread.currentThread();   2 當執行緒目標run()方法結束時該執行緒完成。   3 一旦執行緒啟動,它就永遠不能再重新啟動。只有一個新的執行緒可以被啟動,並且只能一次。一個可執行的執行緒或死執行緒可以被重新啟動。   4 執行緒的排程是JVM的一部分,在一個CPU的機器上上,實際上一次只能執行一個執行緒。一次只有一個執行緒棧執行。JVM執行緒排程程式決定實際執行哪個處於可執行狀態的執行緒。眾多可執行執行緒中的某一個會被選中做為當前執行緒。可執行執行緒被選擇執行的順序是沒有保障的。   5 儘管通常採用佇列形式,但這是沒有保障的。佇列形式是指當一個執行緒完成“一輪”時,它移到可執行佇列的尾部等待,直到它最終排隊到該佇列的前端為止,它才能被再次選中。事實上,我們把它稱為可執行池而不是一個可執行佇列,目的是幫助認識執行緒並不都是以某種有保障的順序排列一個佇列的事實。   6 儘管我們沒有無法控制執行緒排程程式,但可以通過別的方式來影響執行緒排程的方式。 六 示例   當點選start按鈕時,程式從螢幕左上角彈出一個球,這個球開始移動,呼叫addBall方法,迴圈執行1000次move。每呼叫一次move,球就會移動一點,當碰到牆壁時,就會調整方向,但是這個程式有個弊端:當你想在移動1000次之前,就想退出程式,點選close發現,其仍在移動。   Thread類的靜態方法sleep()將暫停給定的毫秒數。呼叫Thread.sleep不會建立一個新執行緒,sleep是Thread類的靜態方法,用於暫停當前執行緒活動。   Bounce.java
 1 package Thread;
 2 import java.awt.*;
 3 import java.awt.event.*;
 4 import javax.swing.*;
 5 public class BounceThread {
 6     public static void main(String[] args){
 7         EventQueue.invokeLater(new Runnable(){
 8             public void run(){
 9                 JFrame frame=new BounceFrame();
10                 frame.setTitle("BounceFrame");
11                 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
12                 frame.setVisible(true);
13             }
14         });
15     }
16 }
17 /*class BallRunnable implements Runnable{
18     private Ball ball;
19     private Component component;
20     public static final int STEPS=1000;
21     public static final int DELAY=5;
22     public BallRunnable(Ball aBall,Component aComponent){
23         ball=aBall;
24         component=aComponent;
25     }
26     public void run(){
27         try{
28             for(int i=1;i<=STEPS;i++){
29                 ball.move(component.getBounds());
30                 component.repaint();
31                 Thread.sleep(DELAY);
32             }
33         }
34         catch(InterruptedException e){}
35     }
36 }*/
37 class BounceFrame extends JFrame{
38     private BallComponent comp;
39     public static final int STEPS=1000;
40     public static final int DELAY=100;
41     public BounceFrame(){
42         comp=new BallComponent();
43         add(comp,BorderLayout.CENTER);
44         JPanel buttonPanel=new JPanel();
45         addButton(buttonPanel,"Start",new ActionListener(){
46             public void actionPerformed(ActionEvent event){
47                 addBall();
48             }
49         });
50         addButton(buttonPanel,"Close",new ActionListener(){
51             public void actionPerformed(ActionEvent event){
52                 System.exit(0);
53             }
54         });
55         add(buttonPanel,BorderLayout.SOUTH);
56         pack();
57     }
58     public void addButton(Container c,String title,ActionListener listener){
59         JButton button=new JButton(title);
60         c.add(button);
61         button.addActionListener(listener);
62     }
63     /*public void addBall(){
64         Ball b=new Ball();
65         comp.add(b);
66         Runnable r=new BallRunnable(b,comp);
67         Thread t=new Thread(r);
68         t.start();
69     }*/
70     public void addBall(){
71         try{
72             Ball ball=new Ball();
73             comp.add(ball);
74             for(int i=1;i<=STEPS;i++){
75                 ball.move(comp.getBounds());
76                 comp.paint(comp.getGraphics());
77                 Thread.sleep(DELAY);
78             }
79         }
80         catch(InterruptedException e){}
81     }
82 }
View Code

  BollComponent.java

 1 package Thread;
 2 import java.awt.*;
 3 
 4 import java.util.*;
 5 import javax.swing.*;
 6 public class BallComponent extends JPanel{
 7     private static final int DEFAULT_WIDTH=450;
 8     private static final int DEFAULT_HEIGHT=350;
 9     private java.util.List<Ball>balls=new ArrayList<>();
10     public void add(Ball b){
11         balls.add(b);
12     }
13     public void paintComponent(Graphics g){
14         super.paintComponent(g);
15         Graphics2D g2=(Graphics2D)g;
16         for(Ball b:balls){
17             g2.fill(b.getShape());
18         }
19     }
20     public Dimension getPreferredSize(){
21         return new Dimension(DEFAULT_WIDTH,DEFAULT_HEIGHT);
22     }
23 }
View Code

  Ball.java

 1 package Thread;
 2 import java.awt.geom.*;
 3 import java.awt.geom.Ellipse2D.Double;
 4 public class Ball {
 5     private static final int XSIZE=15;
 6     private static final int YSIZE=15;
 7     private double x=0;
 8     private double y=0;
 9     private double dx=1;
10     private double dy=1;
11     public void move(Rectangle2D bounds){
12         x+=dx;
13         y+=dy;
14         if(x<bounds.getMinX()){
15             x=bounds.getMinX();
16             dx=-dx;
17         }
18         if(x+XSIZE>=bounds.getMaxX()){
19             x=bounds.getMaxX()-XSIZE;
20             dx=-dx;
21         }
22         if(y<bounds.getMinY()){
23             y=bounds.getMinY();
24             dy=-dy;
25         }
26         if(y+YSIZE>=bounds.getMaxY()){
27             y=bounds.getMaxY()-YSIZE;
28             dy=-dy;
29         }
30     }
31     public Ellipse2D getShape(){
32         return new Ellipse2D.Double(x,y,XSIZE,YSIZE);
33     }
34 }
View Code

  針對上述的情況,下面的程式碼是改進後的,當點選close時,就會退出當前執行緒。而且不論何時點選Start按鈕,addBall都會啟動一個新執行緒.

  實現多個執行緒的方法:將移動球的程式碼放置在一個獨立的執行緒中,點選開始就會重新啟動一個執行緒。簡單過程如下:

    1、將任務程式碼放在實現了Runnable介面的類的run方法中。

1 class MyRunnable implements Runnable{
2      public void run(){
3          task code
4     }   
5 }

    2、建立一個類物件。Runnable r=new MyRunnable();

    3、由Runnable建立一個Thread物件。Thread t=new Thread();

    4、啟動執行緒:t.start();

 BounceThread.java

 1 package Thread;
 2 import java.awt.*;
 3 import java.awt.event.*;
 4 import javax.swing.*;
 5 public class BounceThread {
 6     public static void main(String[] args){
 7         EventQueue.invokeLater(new Runnable(){
 8             public void run(){
 9                 JFrame frame=new BounceFrame();
10                 frame.setTitle("BounceFrame");
11                 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
12                 frame.setVisible(true);
13             }
14         });
15     }
16 }
17 class BallRunnable implements Runnable{
18     private Ball ball;
19     private Component component;
20     public static final int STEPS=1000;
21     public static final int DELAY=5;
22     public BallRunnable(Ball aBall,Component aComponent){
23         ball=aBall;
24         component=aComponent;
25     }
26     public void run(){
27         try{
28             for(int i=1;i<=STEPS;i++){
29                 ball.move(component.getBounds());
30                 component.repaint();
31                 Thread.sleep(DELAY);
32             }
33         }
34         catch(InterruptedException e){}
35     }
36 }
37 class BounceFrame extends JFrame{
38     private BallComponent comp;
39     //public static final int STEPS=1000;
40     //public static final int DELAY=100;
41     public BounceFrame(){
42         comp=new BallComponent();
43         add(comp,BorderLayout.CENTER);
44         JPanel buttonPanel=new JPanel();
45         addButton(buttonPanel,"Start",new ActionListener(){
46             public void actionPerformed(ActionEvent event){
47                 addBall();
48             }
49         });
50         addButton(buttonPanel,"Close",new ActionListener(){
51             public void actionPerformed(ActionEvent event){
52                 System.exit(0);
53             }
54         });
55         add(buttonPanel,BorderLayout.SOUTH);
56         pack();
57     }
58     public void addButton(Container c,String title,ActionListener listener){
59         JButton button=new JButton(title);
60         c.add(button);
61         button.addActionListener(listener);
62     }
63     public void addBall(){
64         Ball b=new Ball();
65         comp.add(b);
66         Runnable r=new BallRunnable(b,comp);
67         Thread t=new Thread(r);
68         t.start();
69     }
70     /*public void addBall(){
71         try{
72             Ball ball=new Ball();
73             comp.add(ball);
74             for(int i=1;i<=STEPS;i++){
75                 ball.move(comp.getBounds());
76                 comp.paint(comp.getGraphics());
77                 Thread.sleep(DELAY);
78             }
79         }
80         catch(InterruptedException e){}
81     }*/
82 }
View Code

 BollComponent.java

 1 package Thread;
 2 import java.awt.*;
 3 
 4 import java.util.*;
 5 import javax.swing.*;
 6 public class BallComponent extends JPanel{
 7     private static final int DEFAULT_WIDTH=450;
 8     private static final int DEFAULT_HEIGHT=350;
 9     private java.util.List<Ball>balls=new ArrayList<>();
10     public void add(Ball b){
11         balls.add(b);
12     }
13     public void paintComponent(Graphics g){
14         super.paintComponent(g);
15         Graphics2D g2=(Graphics2D)g;
16         for(Ball b:balls){
17             g2.fill(b.getShape());
18         }
19     }
20     public Dimension getPreferredSize(){
21         return new Dimension(DEFAULT_WIDTH,DEFAULT_HEIGHT);
22     }
23 }
View Code

  Ball.java 

 1 package Thread;
 2 import java.awt.geom.*;
 3 import java.awt.geom.Ellipse2D.Double;
 4 public class Ball {
 5     private static final int XSIZE=15;
 6     private static final int YSIZE=15;
 7     private double x=0;
 8     private double y=0;
 9     private double dx=1;
10     private double dy=1;
11     public void move(Rectangle2D bounds){
12         x+=dx;
13         y+=dy;
14         if(x<bounds.getMinX()){
15             x=bounds.getMinX();
16             dx=-dx;
17         }
18         if(x+XSIZE>=bounds.getMaxX()){
19             x=bounds.getMaxX()-XSIZE;
20             dx=-dx;
21         }
22         if(y<bounds.getMinY()){
23             y=bounds.getMinY();
24             dy=-dy;
25         }
26         if(y+YSIZE>=bounds.getMaxY()){
27             y=bounds.getMaxY()-YSIZE;
28             dy=-dy;
29         }
30     }
31     public Ellipse2D getShape(){
32         return new Ellipse2D.Double(x,y,XSIZE,YSIZE);
33     }
34 }
View Code

執行結果如下: