您好,登錄后才能下訂單哦!
這篇文章主要講解了JAVA多線程的用法,內(nèi)容清晰明了,對(duì)此有興趣的小伙伴可以學(xué)習(xí)一下,相信大家閱讀完之后會(huì)有幫助。
線程概念
進(jìn)程:?jiǎn)?dòng)一個(gè)應(yīng)用程序就叫一個(gè)進(jìn)程。 接著又啟動(dòng)一個(gè)應(yīng)用程序,這叫兩個(gè)進(jìn)程。每個(gè)進(jìn)程都有一個(gè)獨(dú)立的內(nèi)存空間;進(jìn)程也是程序的一次執(zhí)行過程,是系統(tǒng)運(yùn)行程序的基本單位;系統(tǒng)運(yùn)行一個(gè)程序即是一個(gè)進(jìn)程從創(chuàng)建、運(yùn)行到消亡的過程。
線程:線程是在進(jìn)程內(nèi)部同時(shí)做的事情,一個(gè)進(jìn)程中可以有多個(gè)線程,這個(gè)應(yīng)用程序也可以稱之為多線程程序。
一個(gè)程序運(yùn)行后至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程中可以包含多個(gè)線程
線程調(diào)度:
創(chuàng)建多線程
方法一:創(chuàng)建Thread類的子類
//方法一: //定義Thread類的子類,并重寫該類的run()方法 public class MyThreadDemo01 extends Thread { @Override public void run() { for (int i = 0; i < 20 ; i++) { System.out.println(getName()+"-->"+i); } } }
//主線程 public class MainThread01 { public static void main(String[] args) { //創(chuàng)建Thread子類的實(shí)例,即創(chuàng)建了線程對(duì)象 MyThreadDemo01 thread01 = new MyThreadDemo01(); //調(diào)用線程對(duì)象的start()方法來啟動(dòng)該線程 thread01.start(); for (int i = 0; i < 10 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }
public static Thread currentThread() :返回對(duì)當(dāng)前正在執(zhí)行的線程對(duì)象的引用。
public String getName() :獲取當(dāng)前線程名稱。
public void start() :導(dǎo)致此線程開始執(zhí)行; Java虛擬機(jī)調(diào)用此線程的run方法。
public void run() :此線程要執(zhí)行的任務(wù)在此處定義代碼。
public static void sleep(long millis) :使當(dāng)前正在執(zhí)行的線程以指定的毫秒數(shù)暫停(暫時(shí)停止執(zhí)行)。
方法二:實(shí)現(xiàn)Runnable接口
//方法二: //定義Runnable接口的實(shí)現(xiàn)類,并重寫該接口的run()方法,設(shè)置線程任務(wù) public class MyThreadDemo02 implements Runnable{ @Override public void run() { for (int i = 0; i < 10 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }
//主線程 public class MainThread { public static void main(String[] args) { //創(chuàng)建Runnable實(shí)現(xiàn)類對(duì)象 MyThreadDemo02 runnable = new MyThreadDemo02(); //創(chuàng)建Thread類的對(duì)象,并且該對(duì)象構(gòu)造方法中傳遞Runnable實(shí)現(xiàn)類對(duì)象 Thread thread02 = new Thread(runnable); //調(diào)用Thread對(duì)象的start()方法來啟動(dòng)線程 thread02.start(); for (int i = 0; i < 20 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }
方法三:匿名內(nèi)部類方式
//方法三:匿名內(nèi)部類 public class MainThread { public static void main(String[] args) { //Thread方式 new Thread(){ @Override public void run() { for (int i = 0; i < 10 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }.start(); //Runnable接口方式 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }).start(); //////////////////////////////////////////////// for (int i = 0; i < 20 ; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }
線程安全問題
多線程訪問共享數(shù)據(jù),,且多個(gè)線程中對(duì)資源有寫的操作,就會(huì)出現(xiàn)線程安全問題
線程安全問題都是由全局變量及靜態(tài)變量引起的。若每個(gè)線程中對(duì)全局變量、靜態(tài)變量只有讀操作,而無(wú)寫操作,一般來說,這個(gè)全局變量是線程安全的;若有多個(gè)線程同時(shí)執(zhí)行寫操作,一般都需要考慮線程同步, 否則的話就可能影響線程安全。
解決線程安全問題采用線程同步機(jī)制,主要有以下三種方式:
同步代碼塊
同步代碼塊:synchronized 關(guān)鍵字可以用于方法中的某個(gè)區(qū)塊中,表示只對(duì)這個(gè)區(qū)塊的資源實(shí)行互斥訪問。
public class SafeRunnableDemo implements Runnable { private int ticket = 100; //同步代碼塊 //創(chuàng)建鎖對(duì)象 Object lock = new Object(); @Override public void run() { while (true){ //鎖住同步代碼塊 synchronized (lock){ if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "正在賣第" + ticket + "張"); ticket--; } } } } }
同步方法
同步方法:使用synchronized修飾的方法,就叫做同步方法,保證A線程執(zhí)行該方法的時(shí)候,其他線程只能在方法外等著
public class SafeRunnableDemo implements Runnable { private int ticket = 100; //同步方法 //定義一個(gè)同步方法 public synchronized void lock(){ //同步方法鎖住代碼塊 if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "正在賣第" + ticket + "張"); ticket--; } } //重寫run并使用同步方法 @Override public void run() { while (true){ lock(); } } }
Lock鎖
Lock提供了比synchronized更廣泛的鎖操作
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class SafeRunnableDemo implements Runnable { private int ticket = 100; //Lock鎖方法 //創(chuàng)建ReentrantLock對(duì)象 Lock lock = new ReentrantLock(); @Override public void run() { while (true){ //在可能出現(xiàn)問題的代碼塊之前用lock()方法 lock.lock(); if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "正在賣第" + ticket + "張"); ticket--; } //在可能出現(xiàn)問題的代碼塊之后用unlock()方法 lock.unlock(); } } }
線程機(jī)制
一個(gè)調(diào)用了某個(gè)對(duì)象的 Object.wait() 方法的線程會(huì)等待另一個(gè)線程調(diào)用此對(duì)象Object.notify()方法 或 Object.notifyAll()方法。
其實(shí)waiting狀態(tài)并不是一個(gè)線程的操作,它體現(xiàn)的是多個(gè)線程間的通信,可以理解為多個(gè)線程之間的協(xié)作關(guān)系, 多個(gè)線程會(huì)爭(zhēng)取鎖,同時(shí)相互之間又存在協(xié)作關(guān)系。
當(dāng)多個(gè)線程協(xié)作時(shí),比如A,B線程,如果A線程在Runnable(可運(yùn)行)狀態(tài)中調(diào)用了wait()方法那么A線程就進(jìn)入 了Waiting(無(wú)限等待)狀態(tài),同時(shí)失去了同步鎖。假如這個(gè)時(shí)候B線程獲取到了同步鎖,在運(yùn)行狀態(tài)中調(diào)用了 notify()方法,那么就會(huì)將無(wú)限等待的A線程喚醒。注意是喚醒,如果獲取到鎖對(duì)象,那么A線程喚醒后就進(jìn)入 Runnable(可運(yùn)行)狀態(tài);如果沒有獲取鎖對(duì)象,那么就進(jìn)入到Blocked(鎖阻塞狀態(tài))。
public class WaitAndSleep { public static void main(String[] args) { //創(chuàng)建鎖對(duì)象 Object lock = new Object(); //匿名內(nèi)部類創(chuàng)建線程1 new Thread(){ @Override public void run() { System.out.println(Thread.currentThread().getName()+"需要買票"); //用同步代碼塊包裹 synchronized (lock){ try { //lock.wait(5000);//到5秒自動(dòng)醒來 lock.wait();//進(jìn)入無(wú)限等待,需要喚醒 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+"買到了票"); } }.start(); //匿名內(nèi)部類創(chuàng)建線程2 new Thread(){ @Override public void run() { try { Thread.sleep(5000);//等待5秒 System.out.println(Thread.currentThread().getName()+"出票了"); } catch (InterruptedException e) { e.printStackTrace(); } //用同步代碼塊包裹 synchronized (lock){ lock.notify();//如果有多個(gè)線程等待,隨機(jī)喚醒一個(gè) //lock.notifyAll();//喚醒所有等待的線程 } } }.start(); } }
線程池
當(dāng)在系統(tǒng)中用到了很多的線程,大量的啟動(dòng)和結(jié)束動(dòng)作會(huì)導(dǎo)致系統(tǒng)的性能變卡,響應(yīng)變慢,采用線程池可以解決這個(gè)問題。線程池就相當(dāng)于一個(gè)容器(如同ArrayList),執(zhí)行的任務(wù)放入線程池中,多出來的任務(wù)就等待線程池中的任務(wù)執(zhí)行完再放入。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; //線程池 public class ThreadPoolMain { public static void main(String[] args) { //使用線程池的工廠類 Executors里的靜態(tài)方法 newFixedThreadPool // 生產(chǎn)指定線程數(shù)量的線程池,返回為ExecutorService接口 ExecutorService es = Executors.newFixedThreadPool(2); //調(diào)用ExecutorService中的submit方法,傳遞線程任務(wù),開啟線程 es.submit(new ThreadPoolDemo01()); } } ////////////////////////////////////////////////////// //創(chuàng)建一個(gè)類實(shí)現(xiàn)Runnable接口,重寫run方法,設(shè)置線程任務(wù) public class ThreadPoolDemo01 implements Runnable{ @Override public void run() { ... } }
看完上述內(nèi)容,是不是對(duì)JAVA多線程的用法有進(jìn)一步的了解,如果還想學(xué)習(xí)更多內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。