馬凱軍201771010116《面向物件與程式設計Java》第十六週知識學習總結
一:理論知識部分
1.執行緒的概念:
程式是一段靜態的程式碼,它是應用程式執行的藍 本。 ‐程序是程式的一次動態執行,它對應了從程式碼加 載、執行至執行完畢的一個完整過程。
多執行緒是程序執行過程中產生的多條執行線索。 ‐執行緒是比程序執行更小的單位。 ‐執行緒不能獨立存在,必須存在於程序中,同一進 程的各執行緒間共享程序空間的資料。 ‐每個執行緒有它自身的產生、存在和消亡的過程, 是一個動態的概念。 ‐多執行緒意味著一個程式的多行語句可以看上去幾 乎在同一時間內同時執行。
(2)Java中實現多執行緒的途徑有兩種:
‐建立Thread類的子類
<1>用Thread類的子類建立執行緒
首先需從Thread類派生出一個子類,在該子類中 重寫run()方法。 例: class hand extends Thread { public void run() {……} }
<2>然後用建立該子類的物件 Lefthand left=new Lefthand(); Righthand right=new Righthand(); <3>最後用start()方法啟動執行緒 left.start(); right.start();
用Thread類的子類建立多執行緒的關鍵性操作
–定義Thread類的子類並實現使用者執行緒操作,即
run()方法的實現。
–在適當的時候啟動執行緒。
由於Java只支援單重繼承,用這種方法定義的類不可再繼承其他父類。
‐在程式中定義實現Runnable介面的類
(2)用Runnable()介面實現執行緒:
⚫ 首先設計一個實現Runnable介面的類; ⚫ 然後在類中根據需要重寫run方法; ⚫ 再建立該類物件,以此物件為引數建立Thread 類的物件; ⚫ 呼叫Thread類物件的start方法啟動執行緒,將 CPU執行權轉交到run方法。
2.中斷執行緒:
⚫ 當執行緒的run方法執行方法體中最後一條語句後, 或者出現了在run方法中沒有捕獲的異常時,線 程將終止,讓出CPU使用權。
⚫ 呼叫interrupt()方法也可終止執行緒。 void interrupt() – 向一個執行緒傳送一箇中斷請求,同時把這個線 程的“interrupted”狀態置為true。 – 若該執行緒處於 blocked 狀 態 , 會丟擲 InterruptedException。
(2)測試執行緒是否被中斷的方法
Java提供了幾個用於測試執行緒是否被中斷的方法。 ⚫ static boolean interrupted() – 檢測當前執行緒是否已被中斷 , 並重置狀態 “interrupted”值為false。 ⚫ boolean isInterrupted() – 檢測當前執行緒是否已被中斷 , 不改變狀態 “interrupted”值 。
3.執行緒狀態:
利用各執行緒的狀態變換,可以控制各個執行緒輪流 使用CPU,體現多執行緒的並行性特徵。 ⚫ 執行緒有如下7種狀態: ➢ New (新建)
⚫ new(新建) 執行緒物件剛剛建立,還沒有啟動,此時執行緒 還處於不可執行狀態。例如: Thread thread=new Thread(r); 此時執行緒thread處於新建狀態,有了相應的 記憶體空間以及其它資源。
➢ Runnable (可執行)
⚫ runnable(可執行狀態) ➢ 此時執行緒已經啟動,處於執行緒的run()方法之 中。 ➢ 此時的執行緒可能執行,也可能不執行,只要 CPU一空閒,馬上就會執行。 ➢ 呼叫執行緒的start()方法可使執行緒處於“可運 行”狀態。例如: thread.start();
➢ Running(執行)
➢ Blocked (被阻塞)
⚫ blocked (被阻塞) ➢ 一個正在執行的執行緒因特殊原因,被暫停執行, 進入阻塞狀態。 ➢ 阻塞時執行緒不能進入佇列排隊,必須等到引起 阻塞的原因消除,才可重新進入排隊佇列。 ➢ 引起阻塞的原因很多,不同原因要用不同的方 法解除。 ⚫ sleep(),wait()是兩個常用引起執行緒阻塞的方法。
➢ Waiting (等待)
⚫ 等待阻塞 -- 通過呼叫執行緒的wait()方法,讓線 程等待某工作的完成。
➢ Timed waiting (計時等待) ➢ Terminated (被終止)
⚫ Terminated (被終止) 執行緒被終止的原因有二: ➢ 一是run()方法中最後一個語句執行完畢而自 然死亡。 ➢ 二是因為一個沒有捕獲的異常終止了run方法 而意外死亡。 ➢ 可以呼叫執行緒的 stop 方 法 殺 死 一 個 線 程 (thread.stop();),但是,stop方法已過時, 不要在自己的程式碼中呼叫它。
其他判斷和影響執行緒狀態的方法
➢join():等待指定執行緒的終止。
➢join(long millis):經過指定時間等待終止指定 的執行緒。 ➢isAlive():測試當前執行緒是否在活動。 ➢yield():讓當前執行緒由“執行狀態”進入到“就 緒狀態”從而讓其它具有相同優先順序的等待執行緒 獲取執行權。
4.多執行緒排程:
– Java提供一個執行緒排程器來監控程式啟動後進入 可執行狀態的所有執行緒。執行緒排程器按照執行緒的 優先順序決定應排程哪些執行緒來執行。 – 處於可執行狀態的執行緒首先進入就緒佇列排隊等 候處理器資源,同一時刻在就緒佇列中的執行緒可 能有多個。Java的多執行緒系統會給每個執行緒自動 分配一個執行緒的優先順序。
⚫ Java 的執行緒排程採用優先順序策略: ➢ 優先順序高的先執行,優先順序低的後執行; ➢ 多執行緒系統會自動為每個執行緒分配一個優先順序,預設 時,繼承其父類的優先順序; ➢ 任務緊急的執行緒,其優先順序較高; ➢ 同優先順序的執行緒按“先進先出”的佇列原則;
⚫ 下面幾種情況下,當前執行執行緒會放棄CPU: – 執行緒呼叫了yield() 或sleep() 方法; – 搶先式系統下,有高優先順序的執行緒參與排程; – 由於當前執行緒進行I/O訪問、外存讀寫、等待用 戶輸入等操作導致執行緒阻塞;或者是為等候一 個條件變數,以及執行緒呼叫wait() 方法。
多執行緒併發執行中的問題:
◆多個執行緒相對執行的順序是不確定的。 ◆執行緒執行順序的不確定性會產生執行結果的不 確定性。 ◆在多執行緒對共享資料操作時常常會產生這種不 確定性。
5.執行緒同步:
多執行緒併發執行不確定性問題解決方案:引入線 程同步機制,使得另一執行緒要使用該方法,就只 能等待。
執行緒同步該部分將在下週詳細講述。
實驗十六 執行緒技術
實驗時間 2017-12-8
1、實驗目的與要求
(1) 掌握執行緒概念;
(2) 掌握執行緒建立的兩種技術;
(3) 理解和掌握執行緒的優先順序屬性及排程方法;
(4) 掌握執行緒同步的概念及實現技術;
2、實驗內容和步驟
實驗1:測試程式並進行程式碼註釋。
測試程式1:
l 在elipse IDE中除錯執行ThreadTest,結合程式執行結果理解程式;
l 掌握執行緒概念;
l 掌握用Thread的擴充套件類實現執行緒的方法;
l 利用Runnable介面改造程式,掌握用Runnable介面建立執行緒的方法。
class Lefthand extends Thread { public void run() { for(int i=0;i<=5;i++) { System.out.println("You are Students!"); try{ sleep(500); } catch(InterruptedException e) { System.out.println("Lefthand error.");} } } } class Righthand extends Thread { public void run() { for(int i=0;i<=5;i++) { System.out.println("I am a Teacher!"); try{ sleep(300); } catch(InterruptedException e) { System.out.println("Righthand error.");} } } } public class ThreadTest { static Lefthand left; static Righthand right; public static void main(String[] args) { left=new Lefthand(); right=new Righthand(); left.start(); right.start(); } } |
方法二:用Runnable實現以上程式
package dfg; class Lefthand implements Runnable { public void run() { for(int i=0;i<=5;i++) { System.out.println("You are Students!"); try{ Thread.sleep(500); } catch(InterruptedException e) { System.out.println("Lefthand error.");} } } } class Righthand implements Runnable { public void run() { for(int i=0;i<=5;i++) { System.out.println("I am a Teacher!"); try{ Thread.sleep(300); } catch(InterruptedException e) { System.out.println("Righthand error.");} } } } public class ThreadTest { static Thread left; static Thread right; public static void main(String[] args) { Runnable a=new Lefthand(); Runnable b=new Righthand(); left=new Thread(a); right=new Thread(b); left.start(); right.start(); } }
測試程式2:
l 在Elipse環境下除錯教材625頁程式14-1、14-2 、14-3,結合程式執行結果理解程式;
l 在Elipse環境下除錯教材631頁程式14-4,結合程式執行結果理解程式;
l 對比兩個程式,理解執行緒的概念和用途;
l 掌握執行緒建立的兩種技術。
程式的前兩個部分相同,在14-4中不再贅述
package bounceThread;
import java.awt.geom.*;
/** A ball that moves and bounces off the edges of a rectangle * @version 1.33 2007-05-17 * @author Cay Horstmann */ public class Ball//該類主要定義了小球的大小及其屬性 { private static final int XSIZE = 15; private static final int YSIZE = 15; private double x = 0; private double y = 0; private double dx = 1; private double dy = 1; /** Moves the ball to the next position, reversing direction if it hits one of the edges */ public void move(Rectangle2D bounds) { x += dx; y += dy; if (x < bounds.getMinX()) { x = bounds.getMinX(); dx = -dx; } if (x + XSIZE >= bounds.getMaxX()) { x = bounds.getMaxX() - XSIZE; dx = -dx; } if (y < bounds.getMinY()) { y = bounds.getMinY(); dy = -dy; } if (y + YSIZE >= bounds.getMaxY()) { y = bounds.getMaxY() - YSIZE; dy = -dy; } } /** Gets the shape of the ball at its current position. */ public Ellipse2D getShape() { return new Ellipse2D.Double(x, y, XSIZE, YSIZE); } }
package bounceThread;
import java.awt.*;
import java.util.*; import javax.swing.*; /** * The component that draws the balls. * @version 1.34 2012-01-26 * @author Cay Horstmann */ public class BallComponent extends JComponent { private static final int DEFAULT_WIDTH = 450; private static final int DEFAULT_HEIGHT = 350; private java.util.List<Ball> balls = new ArrayList<>();//構造一個初始容量為 10 的空列表。 //此介面的使用者可以對列表中每個元素的插入位置進行精確地控制。使用者可以根據元素的整數索引(在列表中的位置)訪問元素,並搜尋列表中的元素。 /** * Add a ball to the panel. * @param b the ball to add */ public void add(Ball b) { balls.add(b); } public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; for (Ball b : balls) { g2.fill(b.getShape()); } } public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); } }
package bounce;
import java.awt.*;
import java.awt.event.*; import javax.swing.*; /** * Shows an animated bouncing ball. * @version 1.34 2015-06-21 * @author Cay Horstmann */ public class Bounce { public static void main(String[] args) { EventQueue.invokeLater(() -> { JFrame frame = new BounceFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }); } } /** * The frame with ball component and buttons. */ class BounceFrame extends JFrame { private BallComponent comp; public static final int STEPS = 1000; public static final int DELAY = 3; /** * Constructs the frame with the component for showing the bouncing ball and * Start and Close buttons */ public BounceFrame() { setTitle("Bounce"); comp = new BallComponent(); add(comp, BorderLayout.CENTER);//將自建的swing元件放在居中的位置 JPanel buttonPanel = new JPanel(); addButton(buttonPanel, "Start", event -> addBall()); addButton(buttonPanel, "Close", event -> System.exit(0));//將兩個元件註冊為事件源並新增到buttonpanel中 add(buttonPanel, BorderLayout.SOUTH);//將一個buttonpanel放到框架的南端 pack(); } /** * Adds a button to a container. * @param c the container * @param title the button title * @param listener the action listener for the button */ public void addButton(Container c, String title, ActionListener listener) { JButton button = new JButton(title); c.add(button); button.addActionListener(listener); }//用addbutton方法將兩個按鈕組合為一個整體 /** * Adds a bouncing ball to the panel and makes it bounce 1,000 times. */ public void addBall() { try { Ball ball = new Ball(); comp.add(ball); for (int i = 1; i <= STEPS; i++) { ball.move(comp.getBounds()); comp.paint(comp.getGraphics()); Thread.sleep(DELAY);//呼叫sleep並未建立執行緒物件,只是為了將兩個球的軌跡可以顯示出來 } } catch (InterruptedException e) { } } }
彌補缺陷後的程式(14-4):
package bounceThread;
import java.awt.*;
import java.awt.event.*; import javax.swing.*; /** * Shows animated bouncing balls. * @version 1.34 2015-06-21 * @author Cay Horstmann */ public class BounceThread { public static void main(String[] args) { EventQueue.invokeLater(() -> { JFrame frame = new BounceFrame(); frame.setTitle("BounceThread"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }); } } /** * The frame with panel and buttons. */ class BounceFrame extends JFrame { private BallComponent comp; public static final int STEPS = 1000; public static final int DELAY = 5; /** * Constructs the frame with the component for showing the bouncing ball and * Start and Close buttons */ public BounceFrame() { comp = new BallComponent(); add(comp, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(); addButton(buttonPanel, "Start", event -> addBall()); addButton(buttonPanel, "Close", event -> System.exit(0)); add(buttonPanel, BorderLayout.SOUTH); pack(); } /** * Adds a button to a container. * @param c the container * @param title the button title * @param listener the action listener for the button */ public void addButton(Container c, String title, ActionListener listener) { JButton button = new JButton(title); c.add(button); button.addActionListener(listener); } /** * Adds a bouncing ball to the canvas and starts a thread to make it bounce */ public void addBall() { Ball ball = new Ball(); comp.add(ball); Runnable r = () -> { //使用匿名內部類 try { for (int i = 1; i <= STEPS; i++) { ball.move(comp.getBounds()); comp.repaint(); Thread.sleep(DELAY); } } catch (InterruptedException e) { } }; Thread t = new Thread(r); t.start();//使得小球的移動成為其中的一個子執行緒 } }
測試程式3:分析以下程式執行結果並理解程式。
class Race extends Thread { public static void main(String args[]) { Race[] runner=new Race[4]; for(int i=0;i<4;i++) runner[i]=new Race( ); for(int i=0;i<4;i++) runner[i].start( ); runner[1].setPriority(MIN_PRIORITY); runner[3].setPriority(MAX_PRIORITY);} public void run( ) { for(int i=0; i<1000000; i++); System.out.println(getName()+"執行緒的優先順序是"+getPriority()+"已計算完畢!"); } } |
1 class Race extends Thread {
2 public static void main(String args[]) { 3 Race[] runner=new Race[4]; 4 for(int i=0;i<4;i++) runner[i]=new Race( ); 5 for(int i=0;i<4;i++) runner[i].start( ); 6 runner[1].setPriority(MIN_PRIORITY); 7 runner[3].setPriority(MAX_PRIORITY);} 8 public void run( ) { 9 for(int i=0; i<1000000; i++); 10 System.out.println(getName()+"執行緒的優先順序是"+getPriority()+"已計算完畢!"); 11 } 12 }
測試程式4
l 教材642頁程式模擬一個有若干賬戶的銀行,隨機地生成在這些賬戶之間轉移錢款的交易。每一個賬戶有一個執行緒。在每一筆交易中,會從執行緒所服務的賬戶中隨機轉移一定數目的錢款到另一個隨機賬戶。
l 在Elipse環境下除錯教材642頁程式14-5、14-6,結合程式執行結果理解程式;
l 在Elipse環境下除錯教材642頁程式14-5、14-6,結合程式執行結果理解程式;
1 package synch;
2
3 import java.util.*; 4 import java.util.concurrent.locks.*; 5 6 /** 7 * A bank with a number of bank accounts that uses locks for serializing access. 8 * @version 1.30 2004-08-01 9 * @author Cay Horstmann 10 */ 11 public class Bank 12 { 13 private final double[] accounts; 14 private Lock bankLock; 15 private Condition sufficientFunds; 16 17 /** 18 * Constructs the bank. 19 * @param n the number of accounts 20 * @param initialBalance the initial balance for each account 21 */ 22 public Bank(int n, double initialBalance) 23 { 24 accounts = new double[n]; 25 Arrays.fill(accounts, initialBalance);//將指定的 double 值分配給指定 double 型陣列的每個元素 26 bankLock = new ReentrantLock(); 27 sufficientFunds = bankLock.newCondition();//返回繫結到此 Lock 例項的新 Condition 例項 28 } 29 30 /** 31 * Transfers money from one account to another. 32 * @param from the account to transfer from 33 * @param to the account to transfer to 34 * @param amount the amount to transfer 35 */ 36 public void transfer(int from, int to, double amount) throws InterruptedException 37 { 38 bankLock.lock();//獲取鎖 39 try 40 { 41 while (accounts[from] < amount) 42 sufficientFunds.await();//造成當前執行緒在接到訊號或被中斷之前一直處於等待狀態 43 System.out.print(Thread.currentThread()); 44 accounts[from] -= amount; 45 System.out.printf(" %10.2f from %d to %d", amount, from, to); 46 accounts[to] += amount; 47 System.out.printf(" Total Balance: %10.2f%n", getTotalBalance()); 48 sufficientFunds.signalAll();//喚醒所有等待執行緒 49 } 50 finally 51 { 52 bankLock.unlock();//釋放鎖 53 } 54 } 55 56 /** 57 * Gets the sum of all account balances. 58 * @return the total balance 59 */ 60 public double getTotalBalance() 61 { 62 bankLock.lock(); 63 try 64 { 65 double sum = 0; 66 67 for (double a : accounts) 68 sum += a; 69 70 return sum; 71 } 72 finally 73 { 74 bankLock.unlock(); 75 } 76 } 77 78 /** 79 * Gets the number of accounts in the bank. 80 * @return the number of accounts 81 */ 82 public int size() 83 { 84 return accounts.length; 85 } 86 }
1 package synch;
2
3 /** 4 * This program shows how multiple threads can safely access a data structure. 5 * @version 1.31 2015-06-21 6 * @author Cay Horstmann 7 */ 8 public class SynchBankTest 9 { 10 public static final int NACCOUNTS = 100; 11 public static final double INITIAL_BALANCE = 1000; 12 public static final double MAX_AMOUNT = 1000; 13 public static final int DELAY = 10; 14 15 public static void main(String[] args) 16 { 17 Bank bank = new Bank(NACCOUNTS, INITIAL_BALANCE); 18 for (int i = 0; i < NACCOUNTS; i++) 19 { 20 int fromAccount = i; 21 Runnable r = () -> {//用Lambda表示式簡化程式碼 22 try 23 { 24 while (true) 25 { 26 int toAccount = (int) (bank.size() * Math.random()); 27 double amount = MAX_AMOUNT * Math.random(); 28 bank.transfer(fromAccount, toAccount, amount); 29 Thread.sleep((int) (DELAY * Math.random())); 30 } 31 } 32 catch (InterruptedException e) 33 { 34 } 35 }; 36 Thread t = new Thread(r); 37 t.start(); 38 } 39 } 40 }
綜合程式設計練習
程式設計練習1
- 設計一個使用者資訊採集程式,要求如下:
(1) 使用者資訊輸入介面如下圖所示:
(2) 使用者點選提交按鈕時,使用者輸入資訊顯示控制檯介面;
(3) 使用者點選重置按鈕後,清空使用者已輸入資訊;
(4) 點選視窗關閉,程式退出
按 Ctrl+C 複製程式碼 按 Ctrl+C 複製程式碼 按 Ctrl+C 複製程式碼 按 Ctrl+C 複製程式碼 按 Ctrl+C 複製程式碼 按 Ctrl+C 複製程式碼2.建立兩個執行緒,每個執行緒按順序輸出5次“你好”,每個“你好”要標明來自哪個執行緒及其順序號。
package project2;
class Lefthand extends Thread {
public void run() { for(int i=0;i<=4;i++) { System.out.println("1.你好"); try{ sleep(500); } catch(InterruptedException e) { System.out.println("Lefthand error.");} } } } class Righthand extends Thread { public void run() { for(int i=0;i<=4;i++) { System.out.println("2.你好"); try{ sleep(300); } catch(InterruptedException e) { System.out.println("Righthand error.");} } } } public class ThreadText { static Lefthand left; static Righthand right; public static void main(String[] args) { left=new Lefthand(); right=new Righthand(); left.start(); right.start(); } }
3. 完善實驗十五 GUI綜合程式設計練習程式。
實驗總結;
通過本週的學習我學到了執行緒的概念,並且掌握了執行緒建立的兩種技術,理解了執行緒的優先順序屬性及排程方法,學到了終止執行緒的方法,(1)用Thread類的子類建立執行緒(2)用Runnable()介面實現執行緒;理解和掌握了執行緒的優先順序屬性及排程方法,學到了執行緒的七種狀態。