【Java程式設計】Java多執行緒的實現
多執行緒
程式:是一個指令的集合。
程序:正在執行中的程式,是一個靜態的概念。
執行緒:是程序中的一個單一的連續控制流程,執行緒又本稱為輕量級程序。
一個程序可擁有多個並行的執行緒,一個程序中的執行緒共享相同的記憶體單元,記憶體地址空間,可以訪問相同的變數和物件,而且他們從同一堆中分配物件,通訊,資料交換,同步操作。
由於執行緒間的通訊是在同一地址空間上進行的,所以不需要額外的通訊機制,這就使得通訊更簡便,而且資訊傳遞速度也更快。
java多執行緒實現的方法有兩種。
第一種:
繼承Thread類,重寫run方法,建立物件,呼叫start方法啟動多執行緒。
第二種:
實現Runnable介面,重寫run方法,建立物件,呼叫start方法啟動多執行緒。
實現Runnable介面的類不能直接呼叫start方法,需要建立一個Thread物件,然後把物件傳進去,在呼叫start方法。
繼承Thread類開啟多執行緒:
注意一點,在開啟多執行緒的時候需要呼叫start,不能直接呼叫run方法,因為在開啟多執行緒前,需要一些準備工作,這些工作就需要start來做,start在做完這些準備工作後在去呼叫run方法。
實現Runnable介面開啟多執行緒/** * 使用多執行緒列印到1-100 * * @author FengYuan * */ public class Test extends Thread { /* * 繼承Thread類然後重寫run方法 * * @see java.lang.Thread#run() */ @Override public void run() { for (int i = 0; i <= 100; i++) { System.out.println("執行緒:" + i); } } public static void main(String[] args) { Test t = new Test(); t.start();// 注意多執行緒的開啟需要呼叫start,不能直接呼叫run。 for (int i = 0; i <= 100; i++) { System.out.println("main方法::" + i); } } }
實現Runnable介面不能直接呼叫start方法,因為start方法屬於Thread的,所以需要new一個Thread然後把物件傳進去,通過Thread呼叫start方法
/** * 使用多執行緒列印到1-100 * * @author FengYuan * */ public class Test implements Runnable { /* * 實現Runnable介面然後重寫run方法 * * @see java.lang.Runnable#run() */ @Override public void run() { for (int i = 0; i <= 100; i++) { System.out.println("執行緒:" + i); } } public static void main(String[] args) { Test t = new Test(); new Thread(t).start(); // 注意實現Runnable介面物件不可以直接呼叫start。 for (int i = 0; i <= 100; i++) { System.out.println("main方法::" + i); } } }
常見的方法:
sleep 睡眠 讓執行緒睡眠一定的時間。它有兩種睡眠時間,一種是毫秒,一種是納秒
/**
* 多執行緒
*
* @author FengYuan
*
*/
public class Test extends Thread {
/*
* 繼承Thread類然後重寫run方法
*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try {
sleep(1000); // 讓執行緒睡眠1秒,1000毫秒等於一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i <= 100; i++) {
try {
sleep(0, 1000); // 每列印一次讓執行緒睡眠1000納秒,當然速度太快,看不出效果。
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("執行緒:" + i);
}
}
public static void main(String[] args) {
Test t = new Test();
t.start(); // 注意實現Runnable介面物件不可以直接呼叫start。
for (int i = 0; i <= 100; i++) {
System.out.println("main方法::" + i);
}
}
}
join插隊 任何一個執行緒在呼叫join後,那麼他就有優先執行的權利,其他的執行緒都要等到呼叫join方法的執行緒執行完畢,才可以執行。
通過上面的程式碼可以看出run方法在執行的是後遇到了sleep所以睡眠了,這是main繼續執行完畢,但是在呼叫join後main方法就只有等到run方法執行完畢才能繼續執行
/**
* 多執行緒
*
* @author FengYuan
*
*/
public class Test extends Thread {
/*
* 繼承Thread類然後重寫run方法
*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try {
sleep(1000); // 讓執行緒睡眠1秒,1000毫秒等於一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i <= 100; i++) {
try {
sleep(0, 1000); // 每列印一次讓執行緒睡眠1000納秒,當然速度太快,看不出效果。
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("執行緒:" + i);
}
}
public static void main(String[] args) {
Test t = new Test();
t.start(); // 注意實現Runnable介面物件不可以直接呼叫start。
try {
t.join(); // 呼叫join
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i <= 100; i++) {
System.out.println("main方法::" + i);
}
}
}
yield讓步 正在執行的執行緒給給另一個執行緒讓一下,然後繼續執行,只是讓了一步。
呼叫方法:
Thread.yield();
wait 等待 然正在執行的執行緒進入等待,需要被喚醒,沒有喚醒將會一直等待下去,不會自己醒來。
使用方法:
t.wait();
finally 喚醒等待的執行緒
finallyAll喚醒所有等待的執行緒。
t.finally(); // 喚醒一個等待的執行緒
t.finallyAll(); // 喚醒所有等待的執行緒
下面給一個多執行緒使用的例子:
現有100張火車票,讓5個人去賣票,直到賣完為止。
程式碼如下:
/**
* 多執行緒(賣火車票)
*
* @author FengYuan
*
*/
public class Test implements Runnable {
int ticket = 100; // 100張火車票
/*
* 實現Runnable介面然後重寫run方法 因為火車票共享所以必須用Runnable
*
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
// 獲取當前執行緒的名稱
String name = Thread.currentThread().getName();
if (ticket > 0) {
System.out.println(name + "賣出的第" + ticket + "張票");
ticket--; // 每賣出一張減1
} else {
System.out.println("票買完了");
}
}
public static void main(String[] args) {
Test t = new Test();
for (int i = 0; i <= 22; i++) {
new Thread(t, "1號").start();
new Thread(t, "2號").start();
new Thread(t, "3號").start();
new Thread(t, "4號").start();
new Thread(t, "5號").start();
}
}
}
從上面的程式碼執行後我們可以看出賣的票有好多重複的,這就好比多個人同時買到的一張火車票,這樣的話是不是存在很大的問題,所以有給出了一個方法,可以避免這種情況的發生。
synchronize執行緒鎖,給執行緒加一把鎖,在看效果。
程式碼如下:
/**
* 多執行緒(賣火車票)
*
* @author FengYuan
*
*/
public class Test implements Runnable {
int ticket = 100; // 100張火車票
/*
* 實現Runnable介面然後重寫run方法 因為火車票共享所以必須用Runnable
*
* @see java.lang.Runnable#run()
*/
@Override
public synchronized void run() {
// 獲取當前執行緒的名稱
String name = Thread.currentThread().getName();
if (ticket > 0) {
System.out.println(name + "賣出的第" + ticket + "張票");
ticket--; // 每賣出一張減1
} else {
System.out.println("票買完了");
}
}
public static void main(String[] args) {
Test t = new Test();
for (int i = 0; i <= 22; i++) {
new Thread(t, "1號").start();
new Thread(t, "2號").start();
new Thread(t, "3號").start();
new Thread(t, "4號").start();
new Thread(t, "5號").start();
}
}
}
加鎖之後的程式碼我們再次執行就會發現,票沒有重複的了。
這是因為線上程沒有上鎖的情況下,就會有多個執行緒進同時進入方法,這樣就會造成重複,而枷鎖之後,執行緒每次只能執行一個,一個執行緒執行完後才能進入下一個執行緒。