Java結束專案&執行緒之門
Java階段專案
超級微信服務端專案
程序與執行緒
執行緒是開發中應用非常頻繁的技術。在企業面試中也常被臨幸。
程序:
程序是具有一定獨立功能的應用程式關於某個資料集合上的一次執行活動,它是一個動態的概念。程序是系統進行資源分配和排程的一個獨立單位。程序有獨立的地址空間,一個程序崩潰後,在保護模式下不會對其它程序產生影響。一個程序至少有一個執行緒。
執行緒:
執行緒是程序的一個實體,是CPU排程和分配的基本單位,執行緒基本上不擁有系統資源,只擁有一點在執行過程中必不可少的資源(如堆和棧),但是它可與同屬一個程序的其他執行緒共享程序所擁有的全部資源。執行緒的劃分尺度小於程序,使得多執行緒程式的併發性高。
守護執行緒(Daemon):
守護執行緒是一個特殊的執行緒,Daemon執行緒的作用是在程式的執行期間該執行緒在後臺提供一種服務。它只有在所有的非Daemon執行緒執行結束時,Daemon執行緒也會中止執行。如JVM中的垃圾處理機制就是一個典型的Daemon。
public class Test01_DaemonThread {
public static void main(String[] args) {
DaemonThread daemon = new DaemonThread();
daemon.setDaemon(true);// 設定為守護程式
daemon.start();
Thread t = new Thread();
t.start();
Scanner scanner = new Scanner(System.in);
final String str = scanner.nextLine();// 阻塞主執行緒
scanner.close();
new Thread(){// 建立匿名內部類的物件
@Override
public void run() {
while (!str.equals("q")){
System.out.println("工作執行緒執行中...");
try {
Thread.sleep(1000*1);// 執行緒休眠 1000ms=1s
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
static class DaemonThread extends Thread{// 內部類
@Override
public void run() {
System.out.println("守護執行緒啟動");
}
}
}
執行緒的生命週期:
1、new-新建狀態:建立了一個執行緒物件後,該執行緒就處於新建狀態,此時執行緒還未啟動。
2、Runnable–就緒狀態:執行緒排隊等待CPU分配執行時間。
3、Running-執行(正在執行)狀態:執行緒獲取了CPU分配的時間片段,則進入Running狀態,開始執行run()方法中的程式碼。
4、Block-阻塞(掛起)狀態:執行緒在執行過程中會被中斷,目的是讓其它執行緒獲得執行的機會。阻塞結束時,該執行緒將進入Runnable狀態,而非直接進入Running狀態。
5、Dead-死亡狀態:當執行緒的run()方法執行結束,執行緒進入Dead狀態,物件將被垃圾回收,不可再回到Runnable狀態。
執行緒的阻塞與喚醒
阻塞是指暫停一個執行緒的執行以等待某個條件發生(如某資源就緒)
* sleep()和interrupt():使執行緒沉睡固定的毫秒時間,然後再進入Runnable狀態,也可以使用interrupt()方法提前喚醒執行緒
public class Test02_SleepAndInterrupt {
public static void main(String[] args) {
Thread t = new Thread(){// 匿名內部類(繼承自Thread)物件
public void run() {
while(true){
System.out.println("小白在睡覺....");
try {
Thread.sleep(1000*60*10);// 執行緒休眠10分鐘
} catch (InterruptedException e) {// 休眠執行緒被interrupt喚醒時將報異常
System.out.println("是誰喚醒了我?!");
}
}
}
};
t.start();// 啟動執行緒
while(true){
String str = new Scanner(System.in).nextLine();// 建立匿名Scanner物件
System.out.println("甦醒吧,小白!");
t.interrupt();// 喚醒休眠中的執行緒
}
}
}
- 同步鎖(synchronized):*Java用synchronized關鍵字為指定的方法或物件設定同步鎖*。被設定同步鎖的程式碼塊稱為同步程式碼塊。
synchronized(物件)針對記憶體區塊申請記憶體鎖,記憶體鎖是針對相同物件的互斥操作。
public class Test05_Synchronized {
static Integer money = 1000;// 公共變數
public static void main(String[] args) {
// 建立3個執行緒
User xiaobai = new User("xiaobai");
User jia = new User("路人甲");
User yi = new User("路人乙");
// 開啟3個執行緒
xiaobai.start();
jia.start();
yi.start();
}
static class User extends Thread{
public User(String name){
super(name);
}
@Override
public void run() {
// 如果不加鎖,三個物件同時訪問money,可能出現-200結果
synchronized(money){// 加鎖
if(money >= 400){
System.out.println(Thread.currentThread().getName()+"取出了400元。");
money -= 400;
System.out.println("還剩"+money+"元。");
}else{
System.out.println("餘額不足");
}
}
}
}
}
- wait()和notify():執行緒執行了一個物件的wait()方法就會被掛起(阻塞),只有等到其他執行緒執行了該物件的notify()或notifyAll()方法才能將其喚醒。wait()必須和鎖一起使用,wait在釋放CPU的同時,會釋放其中物件鎖的控制,這也是wait和sleep最大的區別。
public class Test06_WaitAndNotify {
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run() {
System.out.println("小白睡覺.....");
synchronized(this){// 物件鎖
try {
wait();// 阻塞執行緒
} catch (InterruptedException e) {
e.printStackTrace();
}
// 喚醒後,順序執行下面的程式碼
System.out.println("是誰呼喚我?!");
}
}
};
t.start();
Scanner scanner = new Scanner(System.in);
System.out.println("輸入任意字串,召喚小白!");
String str = scanner.nextLine();
synchronized(t){
t.notify();// 喚醒執行緒
}
}
}
- 臨界區同步:使用join()方法後,父執行緒會被阻塞,等待子執行緒執行完畢後才會被喚醒
public class Test04_Join {
static int result = 0;
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run() {
for(int i=0;i<=10;i++){// 從1加到10
result+=i;
}
}
};
t.start();
try {
t.join();// 臨界同步,t為子執行緒,main執行緒為父執行緒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(result);// 55
}
}
- 執行緒執行I/O操作:當執行緒中執行I/O操作或進行遠端通訊時,會因為等待相關資源而進入阻塞狀態。例如當執行new Scanner(System.in).nextLine()方法時,執行緒會被掛起直到使用者向控
制臺輸入資料。
生產者-消費者模型
生產者-消費者模型是一個典型的運用執行緒同步機制的模型。在生產者-消費者模型中通過建立兩個執行緒來描述執行緒的同步機制。一個執行緒模擬生產者,負責提供任務;一個執行緒模擬消費者,負責執行任務;任務存放在一個公共的集合中。
為什麼使用生產者-消費者模型?
生產者-消費者模型是一種典型的運用了執行緒同步機制的模型,是一種高複用鬆耦合的模型。在許多情況下我們在使用多執行緒時會遇到執行緒執行時間差的問題,這種非同步操作我們就可以使用生產者-消費者模型來解決。如何實現生產者-消費者模型?
1、建立一個執行緒負責提供執行的任務:生產者
2、建立一個執行緒負責執行任務:消費者
3、任務存放在一個公用的集合中:倉儲- 當倉儲未滿時進行生產,倉滿則停止生產;
- 消費者在倉儲中有產品的時候進行消費,倉空則掛起等待(阻塞狀態);
- 當消費者發現倉儲沒有產品可以消費的時候應該通知生產者去生產;
- 生產者在生產出可消費的產品時應該通知消費者去消費
// 簡單的模擬
public class Test07_CreatorAndCustomer {
static List<String> tasks = new ArrayList<String>();
static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
// 消費者執行緒
Thread t1 = new Thread(){
@Override
public void run() {
boolean flag = false;
while(true){
try {
Thread.sleep(1000*60*60);
} catch (InterruptedException e) {
String s = "";
for(int i=0;i<100000;i++){
s+="a";
}// 模擬延遲執行
while(!tasks.isEmpty()){// 模擬消費者消費
String str = tasks.remove(0);
if(str.equals("q")){// 退出方式
flag = true;
break;
}
System.out.println(str);
}
}
if(flag){// 結束執行緒
break;
}
}
System.out.println("消費者執行緒結束!");
}
};
t1.start();
// 生產者執行緒
Thread t2 = new Thread(){
@Override
public void run() {
while(true){
System.out.println("請輸入字串:");
String str = scanner.nextLine();
tasks.add(str);
t1.interrupt();
}
}
};
t2.setDaemon(true);// 設定為守護程式,當消費者執行緒結束時,生產者執行緒也將中止
t2.start();
}
}
牛刀小試
設計4個執行緒,2個執行緒每次對j增加1,2個執行緒對j每次減少1。
public class HomeWork {
private static int j;
public static void main(String[] args) {
for(int i=0;i<2;i++){// 呼叫四個執行緒對j操作
Thread t1 = new Thread(new Thread_Inc());
Thread t2 = new Thread(new Thread_Dec());
t1.start();
t2.start();
}
}
private static synchronized void inc(){ // 方法加鎖,避免非同步問題
j++;
System.out.println("j+1="+j);
}
private static synchronized void dec(){ // 可以實驗不加鎖時的情況,與加鎖的進行比較
j--;
System.out.println("j-1="+j);
}
static class Thread_Inc extends Thread{// 內部類,+
@Override
public void run() {
for(int i=0;i<5;i++){
inc();
}
}
}
static class Thread_Dec extends Thread{// 內部類,-
@Override
public void run() {
for(int i=0;i<5;i++){
dec();
}
}
}
}