JAVA多線程知識匯總
線程概念
進程:啟動一個應用程序就叫一個進程。 接著又啟動一個應用程序,這叫兩個進程。每個進程都有一個獨立的內(nèi)存空間;進程也是程序的一次執(zhí)行過程,是系統(tǒng)運行程序的基本單位;系統(tǒng)運行一個程序即是一個進程從創(chuàng)建、運行到消亡的過程。
線程:線程是在進程內(nèi)部同時做的事情,一個進程中可以有多個線程,這個應用程序也可以稱之為多線程程序。
一個程序運行后至少有一個進程,一個進程中可以包含多個線程
線程調度:
- 分時調度:所有線程輪流使用 CPU 的使用權,平均分配每個線程占用 CPU 的時間。
- 搶占式調度:優(yōu)先讓優(yōu)先級高的線程使用 CPU,如果線程的優(yōu)先級相同,那么會隨機選擇一個(線程隨機性),Java使用的為搶占式調度。
創(chuàng)建多線程
方法一:創(chuàng)建Thread類的子類
- 創(chuàng)建Thread類的子類,并重寫該類的run()方法,設置線程任務。
- 創(chuàng)建Thread子類的實例,即創(chuàng)建了線程對象
- 調用線程對象的start()方法來啟動該線程
//方法一:
//定義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子類的實例,即創(chuàng)建了線程對象
MyThreadDemo01 thread01 = new MyThreadDemo01();
//調用線程對象的start()方法來啟動該線程
thread01.start();
for (int i = 0; i < 10 ; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
public static Thread currentThread() :返回對當前正在執(zhí)行的線程對象的引用。
public String getName() :獲取當前線程名稱。
public void start() :導致此線程開始執(zhí)行; Java虛擬機調用此線程的run方法。
public void run() :此線程要執(zhí)行的任務在此處定義代碼。
public static void sleep(long millis) :使當前正在執(zhí)行的線程以指定的毫秒數(shù)暫停(暫時停止執(zhí)行)。
方法二:實現(xiàn)Runnable接口
- 定義Runnable接口的實現(xiàn)類,并重寫該接口的run()方法,設置線程任務
- 創(chuàng)建Runnable實現(xiàn)類對象
- 創(chuàng)建Thread類的對象,并且該對象構造方法中傳遞Runnable實現(xiàn)類對象
- 調用Thread對象的start()方法來啟動線程
//方法二:
//定義Runnable接口的實現(xiàn)類,并重寫該接口的run()方法,設置線程任務
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實現(xiàn)類對象
MyThreadDemo02 runnable = new MyThreadDemo02();
//創(chuàng)建Thread類的對象,并且該對象構造方法中傳遞Runnable實現(xiàn)類對象
Thread thread02 = new Thread(runnable);
//調用Thread對象的start()方法來啟動線程
thread02.start();
for (int i = 0; i < 20 ; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
方法三:匿名內(nè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ù),,且多個線程中對資源有寫的操作,就會出現(xiàn)線程安全問題
線程安全問題都是由全局變量及靜態(tài)變量引起的。若每個線程中對全局變量、靜態(tài)變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執(zhí)行寫操作,一般都需要考慮線程同步, 否則的話就可能影響線程安全。
解決線程安全問題采用線程同步機制,主要有以下三種方式:
- 同步代碼塊
- 同步方法
- 鎖機制
同步代碼塊
同步代碼塊:synchronized 關鍵字可以用于方法中的某個區(qū)塊中,表示只對這個區(qū)塊的資源實行互斥訪問。
- 格式:synchronized(鎖對象){ //訪問共享數(shù)據(jù)的代碼 }
- 鎖對象可以是任意類型
- 多個線程對象要使用同一把鎖
- 鎖對象是將同步代碼塊鎖住,只讓線程在同步代碼塊中執(zhí)行
public class SafeRunnableDemo implements Runnable {
private int ticket = 100;
//同步代碼塊
//創(chuàng)建鎖對象
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í)行該方法的時候,其他線程只能在方法外等著
- 格式:修飾符 synchronized 返回值類型 方法名(參數(shù)列表) { //訪問共享數(shù)據(jù)的代碼 }
- 把共享了同步數(shù)據(jù)的代碼抽取出來,放入同步方法中
public class SafeRunnableDemo implements Runnable {
private int ticket = 100;
//同步方法
//定義一個同步方法
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更廣泛的鎖操作
- 在Lock接口中 void lock() 獲取鎖,void unlock() 釋放鎖
- 需要在成員位置處創(chuàng)建ReentraLock對象,在共享數(shù)據(jù)代碼塊之前調用方法lock()獲取鎖,在之后用unlock()方法釋放鎖
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對象
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();
}
}
}
線程機制
- NEW(新建):線程剛被創(chuàng)建,但是并未啟動。還沒調用start()方法。
- Runnable(可運行):線程可以在java虛擬機中運行的狀態(tài),可能正在運行自己代碼,也可能沒有,這取決于操作系統(tǒng)處理器。
- Blocked(鎖阻塞):當一個線程試圖獲取一個對象鎖,而該對象鎖被其他的線程持有,則該線程進入Blocked狀態(tài);當該線程持有鎖時,該線程將變成Runnable狀態(tài)。
- Waiting(無限等待):一個線程在等待另一個線程執(zhí)行一個(喚醒)動作時,該線程進入Waiting狀態(tài)。進入這個狀態(tài)后是不能自動喚醒的,必須等待另一個線程調用notify()或者notifyAll()方法才能夠喚醒。
- Timed Waiting(計時等待):同waiting狀態(tài),有幾個方法有超時參數(shù),調用他們將進入Timed Waiting狀態(tài)。這一狀態(tài) 將一直保持到超時期滿或者接收到喚醒通知。帶有超時參數(shù)的常用方法有Thread.sleep()、Object.wait()。
- Teminated(被終止):因為run方法正常退出而死亡,或者因為沒有捕獲的異常終止了run方法而死亡。
一個調用了某個對象的 Object.wait() 方法的線程會等待另一個線程調用此對象Object.notify()方法 或 Object.notifyAll()方法。
其實waiting狀態(tài)并不是一個線程的操作,它體現(xiàn)的是多個線程間的通信,可以理解為多個線程之間的協(xié)作關系, 多個線程會爭取鎖,同時相互之間又存在協(xié)作關系。
當多個線程協(xié)作時,比如A,B線程,如果A線程在Runnable(可運行)狀態(tài)中調用了wait()方法那么A線程就進入 了Waiting(無限等待)狀態(tài),同時失去了同步鎖。假如這個時候B線程獲取到了同步鎖,在運行狀態(tài)中調用了 notify()方法,那么就會將無限等待的A線程喚醒。注意是喚醒,如果獲取到鎖對象,那么A線程喚醒后就進入 Runnable(可運行)狀態(tài);如果沒有獲取鎖對象,那么就進入到Blocked(鎖阻塞狀態(tài))。
public class WaitAndSleep {
public static void main(String[] args) {
//創(chuàng)建鎖對象
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秒自動醒來
lock.wait();//進入無限等待,需要喚醒
} 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();//如果有多個線程等待,隨機喚醒一個
//lock.notifyAll();//喚醒所有等待的線程
}
}
}.start();
}
}
線程池
當在系統(tǒng)中用到了很多的線程,大量的啟動和結束動作會導致系統(tǒng)的性能變卡,響應變慢,采用線程池可以解決這個問題。線程池就相當于一個容器(如同ArrayList),執(zhí)行的任務放入線程池中,多出來的任務就等待線程池中的任務執(zhí)行完再放入。
- 使用線程池的工廠類 Executors 里的靜態(tài)方法 newFixedThreadPool 生產(chǎn)指定線程數(shù)量的線程池,返回為ExecutorService接口
- 創(chuàng)建一個類實現(xiàn)Runnable接口,重寫run方法,設置線程任務
- 調用ExecutorService中的submit方法,傳遞線程任務,開啟線程
- 銷毀線程池:ExecutorService中的shutdown方法
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);
//調用ExecutorService中的submit方法,傳遞線程任務,開啟線程
es.submit(new ThreadPoolDemo01());
}
}
//////////////////////////////////////////////////////
//創(chuàng)建一個類實現(xiàn)Runnable接口,重寫run方法,設置線程任務
public class ThreadPoolDemo01 implements Runnable{
@Override
public void run() {
...
}
}
以上就是JAVA多線程知識匯總的詳細內(nèi)容,更多關于JAVA多線程的資料請關注腳本之家其它相關文章!
相關文章
hibernate 配置數(shù)據(jù)庫方言的實現(xiàn)方法
這篇文章主要介紹了hibernate 配置數(shù)據(jù)庫方言的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-05-05
Java8新特性之空指針異常的克星Optional類的實現(xiàn)
這篇文章主要介紹了Java8新特性之空指針異常的克星Optional類的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-10-10

