201771010108 -韓臘梅-第十七週學習總結

第十七週學習總結java

 

1、知識總結編程

1.建立線程的2種方法

方式1:繼承java.lang.Thread類,並覆蓋run()方法。優點:編寫簡單;劣勢:沒法繼承其餘父類安全

方式2:實現java.lang.Runnable接口,並實現run()方法。優點:能夠繼承其餘類,多線程能夠共享同一個Thread對象;劣勢:編程方式稍微複雜,如需訪問當前線程,需調用Thread.currentThread()方法數據結構

2. Java建立線程後,調用start()方法和run()的區別

兩種方法的區別多線程

1) start:併發

    用start方法來啓動線程,真正實現了多線程運行,這時無需等待run方法體代碼執行完畢而直接繼續執行下面的代碼。經過調用Thread類的start()方法來啓動一個線程,這時此線程處於就緒(可運行)狀態,並無運行,一旦獲得cpu時間片,就開始執行run()方法,這裏方法run()稱爲線程體,它包含了要執行的這個線程的內容,Run方法運行結束,此線程隨即終止。dom

2) run:jvm

    run()方法只是類的一個普通方法而已,若是直接調用run方法,程序中依然只有主線程這一個線程,其程序執行路徑仍是隻有一條,仍是要順序執行,仍是要等待ide

run方法體執行完畢後纔可繼續執行下面的代碼,這樣就沒有達到寫線程的目的。性能

總結:調用start方法方可啓動線程,而run方法只是thread的一個普通方法調用,仍是在主線程裏執行。

這兩個方法應該都比較熟悉,把須要並行處理的代碼放在run()方法中,start()方法啓動線程將自動調用 run()方法,這是由jvm的內存機制規定的。而且run()方法必須是public訪問權限,返回值類型爲void。

兩種方式的比較 :

實際中每每採用實現Runable接口,一方面由於java只支持單繼承,繼承了Thread類就沒法再繼續繼承其它類,並且Runable接口只有一個run方法;另外一方面經過結果能夠看出實現Runable接口才是真正的多線程。

3.線程的生命週期

線程是一個動態執行的過程,它也有一個從生產到死亡的過程。

(1)生命週期的五種狀態

新建(new Thread)

當建立Thread類的一個實例(對象)時,此線程進入新建狀態(未被啓動)。

例如:Thread t1 = new Threade();

就緒(runnable)

線程已經被啓動(start),正在等待分配CPU時間片,也就是說此事線程正在就緒隊列中排隊等候獲得CPU資源。

例如:t1.start();

運行(running)

線程得到cpuz資源正在執行任務(run()方法),此時除非線程自動放棄CPU資源或者有優先級更高的的線程進入,線程將一直運行到結束!

死亡(dead)

當線程執行完畢或被其它線程殺死,線程就進入死亡狀態,這時線程不可能再進入就緒狀態等待執行。

天然終止:正常運行run()方法後終止

異常終止:調用stop()方法讓一個線程終止運行

堵塞(blocked)

因爲某種緣由致使正在運行的線程讓出CPU並暫停本身的執行,即進入堵塞狀態。

正在睡眠:用sleep(long t) 方法可以使線程進入睡眠方式。一個睡眠着的線程在指定的時間過去可進入就緒狀態。

正在等待:調用wait()方法。(調用notify()方法回到就緒狀態)

被另外一個線程所阻塞:調用suspend()方法。(調用resume()方法恢復)

5.如何實現線程同步?

當多個線程訪問同一個數據時,容易出現線程安全問題,須要某種方式來確保資源在某一時刻只被一個線程使用。須要讓線程同步,保證數據安全

線程同步的實現方案:同步代碼塊和同步方法,均須要使用synchronized關鍵字

線程同步的好處:解決了線程安全問題

線程同步的缺點:性能降低,可能會帶來死鎖

6. 關於同步鎖的更多細節

Java中每一個對象都有一個內置鎖。

當程序運行到非靜態的synchronized同步方法上時,自動得到與正在執行代碼類的當前實例(this實例)有關的鎖。得到一個對象的鎖也稱爲獲取鎖、鎖定對象、在對象上鎖定或在對象上同步。

當程序運行到synchronized同步方法或代碼塊時才該對象鎖才起做用。

一個對象只有一個鎖。因此,若是一個線程得到該鎖,就沒有其餘線程能夠得到鎖,直到第一個線程釋放(或返回)鎖。這也意味着任何其餘線程都不能進入該對象上的synchronized方法或代碼塊,直到該鎖被釋放。

釋放鎖是指持鎖線程退出了synchronized同步方法或代碼塊。

關於鎖和同步,有一下幾個要點:

1)、只能同步方法,而不能同步變量和類;

2)、每一個對象只有一個鎖;當提到同步時,應該清楚在什麼上同步?也就是說,在哪一個對象上同步?

3)、沒必要同步類中全部的方法,類能夠同時擁有同步和非同步方法。

4)、若是兩個線程要執行一個類中的synchronized方法,而且兩個線程使用相同的實例來調用方法,那麼一次只能有一個線程可以執行方法,另外一個須要等待,直到鎖被釋放。也就是說:若是一個線程在對象上得到一個鎖,就沒有任何其餘線程能夠進入(該對象的)類中的任何一個同步方法。

5)、若是線程擁有同步和非同步方法,則非同步方法能夠被多個線程自由訪問而不受鎖的限制。

6)、線程睡眠時,它所持的任何鎖都不會釋放。

7)、線程能夠得到多個鎖。好比,在一個對象的同步方法裏面調用另一個對象的同步方法,則獲取了兩個對象的同步鎖。

8)、同步損害併發性,應該儘量縮小同步範圍。同步不但能夠同步整個方法,還能夠同步方法中一部分代碼塊。

9)、在使用同步代碼塊時候,應該指定在哪一個對象上同步,也就是說要獲取哪一個對象的鎖。例如:

    public int fix(int y) {

        synchronized (this) {

            x = x - y;

        }

        return x;

    }

固然,同步方法也能夠改寫爲非同步方法,但功能徹底同樣的,例如:

    public synchronized int getX() {

        return x++;

    }

    public int getX() {

        synchronized (this) {

            return x;

        }

    }

效果是徹底同樣的。

7. 簡述sleep( )和wait( )有什麼區別?

sleep()是讓某個線程暫停運行一段時間,其控制範圍是由當前線程決定,也就是說,在線程裏面決定.比如如說,我要作的事情是 "點火->燒水->煮麪",而當我點完火以後我不當即燒水,我要休息一段時間再燒.對於運行的主動權是由個人流程來控制。

而wait(),首先,這是由某個肯定的對象來調用的,將這個對象理解成一個傳話的人,當這我的在某個線程裏面說"暫停!",也是 thisObj.wait(),這裏的暫停是阻塞,仍是"點火->燒水->煮飯",thisObj就比如一個監督個人人站在我旁邊,原本該線 程應該執行1後執行2,再執行3,而在2處被那個對象喊暫停,那麼我就會一直等在這裏而不執行3,但正個流程並無結束,我一直想去煮飯,但還沒被容許, 直到那個對象在某個地方說"通知暫停的線程啓動!",也就是thisObj.notify()的時候,那麼我就能夠煮飯了,這個被暫停的線程就會從暫停處 繼續執行。
其實二者均可以讓線程暫停一段時間,可是本質的區別是一個線程的運行狀態控制,一個是線程之間的通信的問題。

2、實驗部分——線程同步控制

1、實驗目的與要求

 

(1) 掌握線程同步的概念及實現技術;

 

(2) 線程綜合編程練習

 

2、實驗內容和步驟

 

實驗1:測試程序並進行代碼註釋。

 

測試程序1:

 

l  在Elipse環境下調試教材651頁程序14-7,結合程序運行結果理解程序;

 

l  掌握利用鎖對象和條件對象實現的多線程同步技術。

package synch;

import java.util.*;
import java.util.concurrent.locks.*;

/**
一個銀行有許多銀行賬戶,使用鎖序列化訪問 * @version 1.30 2004-08-01
 * @author Cay Horstmann
 */
public class Bank
{
   private final double[] accounts;
   private Lock bankLock;
   private Condition sufficientFunds;

   /**
    * 建設銀行。
    * @param n 帳號
    * @param initialBalance 每一個帳戶的初始餘額
    */
   public Bank(int n, double initialBalance)
   {
      accounts = new double[n];
      Arrays.fill(accounts, initialBalance);
      bankLock = new ReentrantLock();
      sufficientFunds = bankLock.newCondition();
   }

   /**
    * 把錢從一個帳戶轉到另外一個帳戶。
    * @param 從帳戶轉帳
    * @param 轉到要轉帳的帳戶
    * @param 請容許我向你轉達
    */
   public void transfer(int from, int to, double amount) throws InterruptedException
   {
      bankLock.lock();
      try
      {
         while (accounts[from] < amount)
            sufficientFunds.await();
         System.out.print(Thread.currentThread());
         accounts[from] -= amount;
         System.out.printf(" %10.2f from %d to %d", amount, from, to);
         accounts[to] += amount;
         System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
         sufficientFunds.signalAll();
      }
      finally
      {
         bankLock.unlock();
      }
   }

   /**
    * 獲取全部賬戶餘額的總和。
    * @return 總餘額
    */
   public double getTotalBalance()
   {
      bankLock.lock();
      try
      {
         double sum = 0;

         for (double a : accounts)
            sum += a;

         return sum;
      }
      finally
      {
         bankLock.unlock();
      }
   }

   /**
    * 獲取銀行中的賬戶數量。
    * @return 帳號
    */
   public int size()
   {
      return accounts.length;
   }
}
Bank
package synch;

/**
 * 這個程序顯示了多個線程如何安全地訪問數據結構。
 * @version 1.31 2015-06-21
 * @author Cay Horstmann
 */
public class SynchBankTest
{
   public static final int NACCOUNTS = 100;
   public static final double INITIAL_BALANCE = 1000;
   public static final double MAX_AMOUNT = 1000;
   public static final int DELAY = 10;
   
   public static void main(String[] args)
   {
      Bank bank = new Bank(NACCOUNTS, INITIAL_BALANCE);
      for (int i = 0; i < NACCOUNTS; i++)
      {
         int fromAccount = i;
         Runnable r = () -> {
            try
            {
               while (true)
               {
                  int toAccount = (int) (bank.size() * Math.random());
                  double amount = MAX_AMOUNT * Math.random();
                  bank.transfer(fromAccount, toAccount, amount);
                  Thread.sleep((int) (DELAY * Math.random()));
               }
            }
            catch (InterruptedException e)
            {
            }            
         };
         Thread t = new Thread(r);
         t.start();
      }
   }
}
SynchBankTest

 

測試程序2:

 

l  在Elipse環境下調試教材655頁程序14-8,結合程序運行結果理解程序;

 

l  掌握synchronized在多線程同步中的應用。

package synch2;

import java.util.*;

/**
 * 具備多個使用同步原語的銀行帳戶的銀行。
 * @version 1.30 2004-08-01
 * @author Cay Horstmann
 */
public class Bank
{
   private final double[] accounts;

   /**
    * 建設銀行。
    * @param n 帳號
    * @param initialBalance 每一個帳戶的初始餘額
    */
   public Bank(int n, double initialBalance)
   {
      accounts = new double[n];
      Arrays.fill(accounts, initialBalance);
   }

   /**
    * 把錢從一個帳戶轉到另外一個帳戶。
    * @param 從帳戶轉帳
    * @param 轉到要轉帳的帳戶
    * @param 請容許我向你轉達
    */
   public synchronized void transfer(int from, int to, double amount) throws InterruptedException
   {
      while (accounts[from] < amount)
         wait();
      System.out.print(Thread.currentThread());
      accounts[from] -= amount;
      System.out.printf(" %10.2f from %d to %d", amount, from, to);
      accounts[to] += amount;
      System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
      notifyAll();
   }

   /**
    * 獲取全部賬戶餘額的總和。
    * @return 總餘額
    */
   public synchronized double getTotalBalance()
   {
      double sum = 0;

      for (double a : accounts)
         sum += a;

      return sum;
   }

   /**
    * 獲取銀行中的賬戶數量。
    * @return 
    */
   public int size()
   {
      return accounts.length;
   }
}
Bank
package synch2;

/**
 * 
 * 這個程序展現了多個線程如何使用同步方法安全地訪問數據結構。
 * @version 1.31 2015-06-21
 * @author Cay Horstmann
 */
public class SynchBankTest2
{
   public static final int NACCOUNTS = 100;
   public static final double INITIAL_BALANCE = 1000;
   public static final double MAX_AMOUNT = 1000;
   public static final int DELAY = 10;

   public static void main(String[] args)
   {
      Bank bank = new Bank(NACCOUNTS, INITIAL_BALANCE);
      for (int i = 0; i < NACCOUNTS; i++)
      {
         int fromAccount = i;
         Runnable r = () -> {
            try
            {
               while (true)
               {
                  int toAccount = (int) (bank.size() * Math.random());
                  double amount = MAX_AMOUNT * Math.random();
                  bank.transfer(fromAccount, toAccount, amount);
                  Thread.sleep((int) (DELAY * Math.random()));
               }
            }
            catch (InterruptedException e)
            {
            }
         };
         Thread t = new Thread(r);
         t.start();
      }
   }
}
SynchBankTest2

 

測試程序3:

 

l  在Elipse環境下運行如下程序,結合程序運行結果分析程序存在問題;

 

l  嘗試解決程序中存在問題。

 

class Cbank

{

     private static int s=2000;

     public   static void sub(int m)

     {

           int temp=s;

           temp=temp-m;

          try {

                     Thread.sleep((int)(1000*Math.random()));

                   }

           catch (InterruptedException e)  {              }

                 s=temp;

                 System.out.println("s="+s);

             }

}

 

 

class Customer extends Thread

{

  public void run()

  {

   for( int i=1; i<=4; i++)

     Cbank.sub(100);

    }

 }

public class Thread3

{

 public static void main(String args[])

  {

   Customer customer1 = new Customer();

   Customer customer2 = new Customer();

   customer1.start();

   customer2.start();

  }

}

 

運行結果:

改正代碼:

class Cbank
{
     private static int s=2000;
     public  synchronized static void sub(int m)
     {
           int temp=s;
           temp=temp-m;
          try {
                 Thread.sleep((int)(1000*Math.random()));
               }
           catch (InterruptedException e)  {              }
              s=temp;
              System.out.println("s="+s);
          }
    }


class Customer extends Thread
{
  public void run()
  {
   for( int i=1; i<=4; i++)
     Cbank.sub(100);
    }
 }

public class Thread3
{
 public static void main(String args[])
  {
   Customer customer1 = new Customer();
  
   Customer customer2 = new Customer();
   customer1.start();
   customer2.start();
  }
}
Cbank

實驗2 編程練習

 

利用多線程及同步方法,編寫一個程序模擬火車票售票系統,共3個窗口,賣10張票,程序輸出結果相似(程序輸出不惟一,能夠是其餘相似結果)。

 

Thread-0窗口售:第1張票

 

Thread-0窗口售:第2張票

 

Thread-1窗口售:第3張票

 

Thread-2窗口售:第4張票

 

Thread-2窗口售:第5張票

 

Thread-1窗口售:第6張票

 

Thread-0窗口售:第7張票

 

Thread-2窗口售:第8張票

 

Thread-1窗口售:第9張票

 

Thread-0窗口售:第10張票

 

代碼:

package 編程一;

public class 售票 {
public static void main(String[] args) {
mythread mythread=new mythread();
Thread t1=new Thread(mythread);
Thread t2=new Thread(mythread);
Thread t3=new Thread(mythread);
t1.start();
t2.start();
t3.start();
}
}
class mythread implements Runnable{
int t=1;
boolean flag=true;
@Override
public void run() {
while(flag) {
try {
Thread.sleep(50);
}catch(InterruptedException e)
{
e.printStackTrace();
}
synchronized (this) {
if(t<=10) {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName() + "窗口售:第" + t + "張票");
t++;
}
if(t>10) {
flag=false;
}
}
}

}

}

 
售票

結果:

3、實驗總結

         這是本學期最後一次實驗,實驗內容也比較少,對線程有了更深的認識,很感謝學長和老師這一學期幫助。

相關文章
相關標籤/搜索