1.請簡單描述什麼是並行,什麼是併發?java
並行:指兩個或多個事件在同一時刻發生(同時發生)。程序員
併發:指兩個或多個事件在同一個時間段內發生。網絡
通俗易懂版:多線程
你吃飯吃到一半,電話來了,你一直到吃完了之後纔去接,這就說明你不支持併發也不 支持並行。
你吃飯吃到一半,電話來了,你停了下來接了電話,接完後繼續吃飯,這說明你支持併發。
你吃飯吃到一半,電話來了,你一邊打電話一邊吃飯,這說明你支持並行。併發
併發的關鍵是你有處理多個任務的能力,不必定要同時。
並行的關鍵是你有同時處理多個任務的能力。jvm
因此它們最關鍵的點就是:是不是『同時』。ide
2.請描述什麼是進程,什麼是線程,進程與線程之間的關係,並舉例說明。測試
進程指正在運行的程序。確切的來講,當一個程序進入內存運行,即變成一個進程,進程是處於運行過程當中的程序,而且具備必定獨立功能。this
線程是進程中的一個執行單元,負責當前進程中程序的執行,一個進程中至少有一個線程。一個進程中是能夠有多個線程的,這個應用程序也能夠稱之爲多線程程序。spa
一個程序運行後至少有一個進程,一個進程中能夠包含多個線程,但一個進程中至少包含一個線程。好比使用迅雷軟件下載網絡文件時,同時下載多個文件,就使用到了多線程下載。
3.自定義異常類
每個學生(Student)都有學號,姓名和分數,分數永遠不能爲負數
若是老師給學生賦值一個負數,拋出一個自定異常
/* 1.定義異常類NoScoreException,繼承RuntimeException a)提供空參和有參構造方法 */ public class NoScoreException extends RuntimeException { // 空參構造 public NoScoreException() { super(); } // 有參構造 public NoScoreException(String message) { super(message); } }
/* 2.定義學生類(Student) a)屬性:name,score b)提供空參構造 c)提供有參構造; i.使用setXxx方法給名稱和score賦值 d)提供setter和getter方法 i.在setScore(int score)方法中 1.首先判斷,若是score爲負數,就拋出NoScoreException,異常信息爲:分數不能爲負數:xxx. 2.而後在給成員score賦值. */ public class Student { private String name; private int score; // 空參構造 public Student() { super(); } // c)提供有參構造; // i.使用setXxx方法給名稱和score賦值 public Student(String name,int score){ setName(name); setScore(score); } // d)提供setter和getter方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getScore() { return score; } // i.在setScore(int score)方法中 public void setScore(int score) { // 1.首先判斷,若是score爲負數,就拋出NoScoreException,異常信息爲:分數不能爲負數:xxx. if(score <0){ throw new NoScoreException(":分數不能爲負數:"+score); } // 2.而後在給成員score賦值. this.score = score; } }
/* 3.定義測試類Test9 a)提供main方法,在main方法中 i.使用滿參構造方法建立Student對象,分數傳入一個負數,運行程序 ii.因爲一旦遇到異常,後面的代碼的將不在執行,因此須要註釋掉上面的代碼 iii.使用空參構造建立Student對象 iv.調用setScore(int score)方法,傳入一個正數,運行程序 v.調用setScore(int score)方法,傳入一個負數,運行程序 */ public class Test9 { public static void main(String[] args) { // i.使用滿參構造方法建立Student對象,分數傳入一個負數,運行程序 // Student s = new Student("景甜", -10); // ii.因爲一旦遇到異常,後面的代碼的將不在執行,因此須要註釋掉上面的代碼 // iii.使用空參構造建立Student對象 Student s = new Student(); // iv.調用setScore(int score)方法,傳入一個正數,運行程序 s.setScore(100); // v.調用setScore(int score)方法,傳入一個負數,運行程序 s.setScore(-5); } }
4.建立多線程對象,開啓多線程。在子線程中輸出1-100之間的偶數,主線程輸出1-100之間的奇數。
//自定義線程類: public class MyThread extends Thread { /** * 重寫run方法,完成該線程執行的邏輯 */ @Override public void run() { for (int i = 1; i <= 100; i++) { if (i % 2 == 0) { System.out.println("子線程打印輸出偶數:" + i); } } } }
//測試類: public class Test11 { public static void main(String[] args) { //建立自定義線程對象 MyThread mt = new MyThread(); //開啓線程 mt.start(); //在主方法中執行for循環 for (int i = 1; i <= 100; i++) { if (i % 2 == 1) { System.out.println("主線程打印輸出奇數:" + i); } } } }
5.
建立三個子線程,在每一個線程中開啓10萬次的循環,線程1循環中將循環自增變量i賦值給Integer類型變量 a,線程2循環中將字符串"程序員"賦值給String類型變量b,線程3循環中將字符串"程序員"和循環自增變量i拼接後賦值給String類型變量c
分別計算三個線程完成任務所用的毫秒值
//線程1 public class Thread1 extends Thread{ @Override public void run() { long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { Integer a = i; } long end = System.currentTimeMillis(); System.out.println("線程1執行時間:"+(end-start)); } }
//線程2 public class Thread2 extends Thread{ @Override public void run() { long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { String b = "程序員"; } long end = System.currentTimeMillis(); System.out.println("線程2執行時間:"+(end-start)); } }
//線程3 public class Thread3 extends Thread{ @Override public void run() { long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) { String c = "程序員"+i; } long end = System.currentTimeMillis(); System.out.println("========線程3執行時間:"+(end-start)); } }
//測試類 public class TestThread { public static void main(String[] args) { new Thread1().start(); new Thread2().start(); new Thread3().start(); } }
6.請描述Thread類中的start()方法與run()方法的區別。
答:
線程對象調用run()方法不開啓線程,僅是對象調用方法。線程對象調用start()方法開啓線程,並讓jvm調用run()方法在開啓的線程中執行。
7.請描述建立線程的兩種方式。
答:
第一種方式是將類聲明爲 Thread 的子類。
第二種方式是聲明一個類實現Runnable 接口。
1. 定義Runnable接口的實現類,並重寫該接口的run()方法,該run()方法的方法體一樣是該線程的線程執行體。
2. 建立Runnable實現類的實例,並以此實例做爲Thread的target來建立Thread對象,Thread對象纔是真正的線程對象。
3. 調用線程對象的start()方法來啓動線程。
8.請編寫程序,分別打印主線程的名稱和子線程的名稱。
要求使用兩種方式實現:
第一種方式:繼承Thread類。
第二種方法:實現Runnable接口。
// 第一種方式:繼承Thread類 /* * 1.定義一個子線程的類,繼承Thread類; */ public class SubThread extends Thread { /* *2.在子線程類中重寫run方法,在run方法中打印子線程的名稱; */ public void run() { // 打印子線程的名稱 System.out.println("subThread:" + Thread.currentThread().getName()); } } /* * 3.定義一個測試類 */ public class ThreadDemo { public static void main(String[] args) { // 4.在main方法中打印主線程的名稱; System.out.println("main:" + Thread.currentThread().getName()); // 5.在main方法中建立子線程對象; SubThread st = new SubThread(); // 6.調用子線程對象的start方法,開啓子線程。 st.start(); } }
// 第二種方式:實現Runnable接口 /* * 1.定義一個子任務類,實現Runnable接口。 */ public class SubRunnable implements Runnable { @Override public void run() { // 2.在子任務類中重寫run方法,在run方法中打印子線程的名稱。 System.out.println("SubRunnable:"+ Thread.currentThread().getName()); } } /* * 3.定義一個測試類。 */ public class RunnableDemo { public static void main(String[] args) { // 4.在main方法中打印主線程的名稱。 System.out.println("RunnableDemo:"+ Thread.currentThread().getName()); // 5.在main方法中建立一個子任務對象。 SubRunnable r = new SubRunnable(); // 6.在main方法中建立一個Thread類的對象,並把子任務對象傳遞給Thread類的 構造方法。 Thread t = new Thread(r); // 7.調用Thread類對象的start方法開啓子線程。 t.start(); } }
9.請描述實現Runnable接口比繼承Thread類所具備的優點:
答:
1. 適合多個相同的程序代碼的線程去共享同一個資源。
2. 能夠避免java中的單繼承的侷限性。
3. 增長程序的健壯性,實現解耦操做,代碼能夠被多個線程共享,代碼和數據獨立。
4. 線程池只能放入實現Runable或callable類線程,不能直接放入繼承Thread的類。
10.請描述在線程的生命週期中, 有幾種狀態呢
答:
線程能夠在java虛擬機中運行的狀態,可能正在運行本身代碼,也可能沒有,這取決於操做系統處理器。
當一個線程試圖獲取一個對象鎖,而該對象鎖被其餘的線程持有,則該線程進入Blocked狀態;當該線程持有鎖時,該線程將變成Runnable狀態。
一個線程在等待另外一個線程執行一個(喚醒)動做時,該線程進入Waiting狀態。進入這個狀態後是不能自動喚醒的,必須等待另外一個線程調用notify或者notifyAll方法纔可以喚醒。
同waiting狀態,有幾個方法有超時參數,調用他們將進入Timed Waiting狀態。這一狀態將一直保持到超時期滿或者接收到喚醒通知。帶有超時參數的經常使用方法有Thread.sleep 、Object.wait。
由於run方法正常退出而死亡,或者由於沒有捕獲的異常終止了run方法而死亡。