Java多執行緒技術概述
一、執行緒與行程
行程:通俗來解釋就是一個程式,一個App,打開任務管理器可以看到當前電腦中正在運行的行程,
執行緒:一個行程中一般包含多個執行緒,打開任務管理器也可以看到當前電腦中正在運行的執行緒每個各自執行自己的任務來實作行程的運行,當一個行程中的最后一個執行緒結束時,整個行程就結束了,
執行緒的6種狀態:
NEW(未啟動的執行緒)
RUNNABLE(執行的執行緒)
BLOCKED(被阻塞的執行緒)
WAITING(無限期等待的執行緒)
TIMED_WAITING(有限期等待的執行緒)
TERMINATED(已結束的執行緒)


二、Java中執行緒創建的三種方式
1.Thread類: 通過創建一個類來繼承Thread類,在這個類中重寫run()方法,通過這個類創建的物件就是一個執行緒,
class MyThread extends Thread{
@Override
public void run() {
//執行的Java陳述句
}
}
public static void main(String[] args) {
MyThread t = new MyThread();
//執行緒啟動
t.start();
}
2.Runnable介面:通過創建一個類來實作Runnable介面,在這個類中重寫run()方法,通過這個類創建的物件是一個執行緒任務,我們將這個任務傳給一個Thread物件即可執行這個執行緒任務,(推薦使用這種方式,傳入一個Runnable任務即可執行執行緒,跟使用執行緒池有關)
class MyRunnable implements Runnable{
@Override
public void run() {
//執行的Java陳述句
}
}
public static void main(String[] args) {
MyRunnable r = new MyRunnable1();
Thread t = new Thread(r);
t.start();
}
3.Callable介面(很少用的方式):創建方式與Runnable相似,創建此類執行緒會產生一個回傳值,如果主程式要獲取這個回傳值,則主程式會在Callable執行緒運行結束后再運行,不獲取的話,則兩個執行緒并行,
三、執行緒中一些常用的方法
Thread的常用方法
1.String getName() //獲取該執行緒的名字
2.void setName(String name) //設定該執行緒的名字
3.void start() //執行緒開始
4.static Thread currentThread() //獲取當前執行緒物件
5.static void sleep(long millis) //讓當前執行緒休眠,進入阻塞狀態,傳入的引數單位為毫秒
6.void setDaemon(boolean on) //將執行緒設定為守護執行緒或者用戶執行緒
7.void interrupt() //中斷此執行緒
8.int getPriority() //回傳此執行緒的優先級
9.void setPriority(int newPriority) //更改此執行緒的優先級
10.Thread(Runnable target) //(構造方法)傳入一個Runnable任務
11.Thread(String name) //(構造方法)為執行緒取一個名字
1、interrupt方法和stop方法
執行緒在運行的程序中兩種中斷執行緒的方法,一個是stop()方法,一個是interrupt()方法,
Sun公司在JDK1.2版本的時候將stop()方法改為過時了,因為這種中斷執行緒的方式是在外部強制的,這可能會導致在中斷程序資料丟失,所以是不安全的,
使用interrupt()方法則是一種安全的方式,當在執行緒外部呼叫interrupt()方法時,JVM會給運行的執行緒傳遞一個例外物件,當執行緒接收到例外物件時,執行緒就會終止,
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
t.interrupt();
}
2、守護執行緒和用戶執行緒的設定
void setDaemon(boolean on)
使用setDaemon()方法可以將一個執行緒設定為守護執行緒或者用戶執行緒(引數為TRUE時,是守護執行緒,引數為FALSE時為用戶執行緒),一個執行緒被創建時默認是用戶執行緒,
當用戶執行緒和守護執行緒一起運行,當用戶執行緒結束時,則守護執行緒就結束,
四、執行緒安全
執行緒異步:即多條執行緒同時執行一個任務時,這種情況下往往是出現資料錯亂的情況,例如兩個人同時對一個銀行賬戶進行取錢,賬戶中有10000元,兩個人同時取走5000元,結果賬戶中還剩余5000元,所以這種執行緒異步的情況是非常不安全的,
執行緒同步:即多條執行緒同時執行一個任務時,,當一個執行緒開始執行任務執行緒后,為這個任務加鎖,其他執行緒等待次執行緒執行完任務執行緒后再進行搶奪執行任務的時間片,
1、實作執行緒安全的方法(以售票視窗買票為例)
1.1、synchronized方法(顯式鎖)
同步代碼塊
當一個售票視窗在賣票時,其他視窗等待,
語法:
synchronized (加鎖物件){
//Java陳述句
}
class Runnable implements Runnable{
private int count = 10;
private Object o = new Object();
public void run() {
while(true){
synchronized (o){
if(count>0){
System.out.println(Thread.currentThread().getName()+"買票中");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("余票:"+count);
}else{
break;
}
}
}
}
}
同步方法
當時用同步方法時,當前加鎖的物件默認為當前物件this
class Runnable implements Runnable{
private int count = 10;
public void run() {
while(true){
boolean flag = sale();
if(!flag){
break;
}
}
}
public synchronized boolean sale(){
//判斷票數是否大于0,是回傳true,否回傳false
if(count>0){
System.out.println(Thread.currentThread().getName()+"買票中");
try {
//此處休眠0.5秒
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//票賣出,票數減一
count--;
System.out.println("余票:"+count);
return true;
}else{
return false;
}
}
}
1.2、Lock方法(隱式鎖)
使用Lock方法需要創建Lock物件,并在需要加鎖是手動加鎖,在需要解鎖時手動解鎖
class Runnable implements Runnable{
private int count = 10;
private Lock l = new ReentrantLock();
public void run() {
while(true){
l.lock();
if(count>0){
System.out.println(Thread.currentThread().getName()+"買票中");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("余票:"+count);
l.unlock();
}else{
l.unlock();
break;
}
}
}
}
1.3、顯式鎖和隱式鎖的區別
(1)Sync:Java中的關鍵字,是由JVM來維護的,是JVM層面的鎖,
(2)Lock:是JDK5以后才出現的具體的類,使用lock是呼叫對應的API,是API層面的鎖,
(3)在使用sync關鍵字的時候,我們使用者根本不用寫其他的代碼,然后程式就能夠獲取和釋放鎖了,那是因為當sync代碼塊執行完成之后,系統會自動的讓程式釋放占用的鎖,Sync是由系統維護的,如果非邏輯問題的話話,是不會出現死鎖的,
(4)在使用lock的時候,我們使用者需要手動的獲取和釋放鎖,如果沒有釋放鎖,就有可能導致出現死鎖的現象,手動獲取鎖方法:lock.lock(),釋放鎖:unlock方法,需要配合tyr/finaly陳述句塊來完成,
(5)Sync是不可中斷的,除非拋出例外或者正常運行完成
(6)Lock可以中斷的,
(7)Sync:非公平鎖
(8)lock:兩者都可以的,默認是非公平鎖,ReentrantLock(boolean fair),true是公平鎖,false是不公平鎖
五、死鎖
概述
A和B兩人分別進入試衣間1和試衣間2,A想等B出來后去試衣間2,自己則在試衣間1中等待,B想等A出來后去試衣間1,自己則在試衣間2中等待,最終2個人都在等對方出來,但是對方都不出來,導致一直僵持,
public class DeadLockTest {
public static void main(String[] args) {
A a = new A();
B b = new B();
//子執行緒中需要和主執行緒中同樣的A和B物件
R r = new R(a,b);
Thread t = new Thread(r);
t.start();
b.say(a);
}
}
class B{
public synchronized void say(A a){
System.out.println("BBBBB");
a.reply();
}
public synchronized void reply(){
System.out.println("bbbbb");
}
}
class A{
public synchronized void say(B b){
System.out.println("AAAAA");
b.reply();
}
public synchronized void reply(){
System.out.println("aaaaa");
}
}
class R implements Runnable{
private A a;
private B b;
public R(A a, B b) {
this.a = a;
this.b = b;
}
public void run() {
a.say(b);
}
}
六、生產者與消費者
當廚師在做菜時,服務員休息狀態,當廚師做完菜后,廚師休息,服務員端菜出去,等服務員端空盤子回來后,服務員繼續休息,叫廚師繼續做菜,依次回圈
public class Test {
public static void main(String[] args) {
Food f = new Food();
Cook c = new Cook(f);
Waiter w = new Waiter(f);
//廚師執行緒
new Thread(c).start();
//服務員執行緒
new Thread(w).start();
}
}
class Cook implements Runnable{
private Food f;
public Cook(Food f) {
this.f = f;
}
@Override
public void run() {
for(int i = 0;i<10;i++){
f.cook(i);
try {
//此處休眠0.5秒
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Waiter implements Runnable{
private Food f;
private boolean flag = true;
public Waiter(Food f) {
this.f = f;
}
@Override
public void run() {
for(int i = 0;i<10;i++){
f.get();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Food{
private String name;
private String tasty;
private boolean flag = true;
public Food() {
}
public Food(String name, String tasty) {
this.name = name;
this.tasty = tasty;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTasty() {
return tasty;
}
public void setTasty(String tasty) {
this.tasty = tasty;
}
//廚師做菜
public synchronized void cook(int i){
if(flag){
if(i % 2 == 0){
this.setName("番茄炒蛋");
this.setTasty("咸");
}else{
this.setName("糖醋排骨");
this.setTasty("酸甜");
}
System.out.println("廚師做菜:"+this.getName()+",味道:"+this.getTasty());
}
flag = false;
//喚醒其他執行緒
this.notifyAll();
try {
//廚師執行緒休眠
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//服務員端菜
public synchronized void get(){
if(!flag){
System.out.println("服務員出菜:"+this.getName()+",味道:"+this.getTasty());
}
flag = true;
//喚醒其他執行緒
this.notifyAll();
try {
//服務員執行緒休眠
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
七、執行緒池(了解)
概述
當程式中需要執行許多的內容很少的執行緒時,執行緒創建所花費的時間就會多于每次執行緒運行的所耗費的時間,就是導致程式的效率大大降低,使用執行緒池的方法就可以為程式節省下創建執行緒的時間,
執行緒池的種類
ExecutorService service = Executors.創建方法
void execute(Runnable command) //指揮執行緒池執行任務
1.快取執行緒池
任務執行緒傳入時自動分配執行緒,執行緒不夠時自動創建新執行緒
Executors.newCachedThreadPool() //創建快取執行緒池
2.定長執行緒池
指定執行緒池執行緒的個數,任務執行緒傳入時自動分配執行緒,執行緒不夠時剩余任務執行緒排隊等待執行緒池中的執行緒執行完畢
Executors.newFixedThreadPool(int nThreads) //創建定長執行緒池,傳入執行緒池中執行緒的個數
3.單執行緒執行緒池
執行緒池中只有一個執行緒,任務執行緒傳入時自動分配執行緒,一個任務執行時剩余任務執行緒排隊等待執行緒池中的執行緒執行完畢
Executors.newSingleThreadExecutor() //創建單執行緒執行緒池
4.周期定長執行緒池
指定執行緒池執行緒的個數,任務執行緒傳入時自動分配執行緒,可以設定任務執行緒第一次執行的延時時間和之后每次執行的間隔時間
Executors.newScheduledThreadPool(int corePoolSize)
//創建周期定長執行緒池,傳入執行緒池中執行緒的個數
service.schedule(Runnable command, long delay, TimeUnit unit)
//執行緒定時執行,傳入任務、時長和時長單位
service.scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
//周期性定時執行,傳入任務,初次執行延時時長,周期間隔時長和時長單位
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/231990.html
標籤:其他
