Java執行緒總結(一)
- 一.執行緒的生命周期
- 1)行程與執行緒的定義和特征
- 2)執行緒的生命周期
- 二.創建執行緒的三種方式
- 1)繼承Thread類
- 2)實作Runnable介面
- 3)實作Callable介面
- 4)Callable與其他兩種方式的區別
- 三.CAS原理與ABA問題
- 四.volatile和synchronized關鍵字
- 1)volatile關鍵字
- 2)synchronized關鍵字
- 五.AtomicInteger與int的區別
- 六.執行緒的三大特性
- 1)原子性
- 2)有序性
- 3)可見性
- 七.死鎖的四個必要條件
- 1)互斥
- 2)不可搶占
- 3)請求與保持
- 4)回圈等待
- 八.可重入鎖
一.執行緒的生命周期
1)行程與執行緒的定義和特征
1.行程的定義:行程是程式運行的一個物體的運行程序,是系統進行資源分配和調配的一個獨立單位,
2.行程的特征:2.1系統開銷:創建撤銷切換開銷大,資源要重新分配和回收,
2.2擁有資產:資源擁有的基本單位,2.3調度:資源分配的基本單位,2.4安全性:行程間相互獨立,互不影響,2.5地址空間:系統賦予的獨立的記憶體地址空間,
3.執行緒的定義:執行緒是行程運行和執行的最小的調度單位,
4.執行緒的特征:4.1系統開銷:僅保存少量暫存器的內容,開銷小,在行程的地址空間執行代碼,4.2:擁有資產:基本不占資源,僅有不可少的資源(程式計數器,一組暫存器和堆疊),4.3:調度:獨立調度分配的單位,4.4:安全性:執行緒共享一個行程下面的資源,可以互相通信和影響,4.5:地址空間:由相關堆疊暫存器和執行緒控制表TCB組成,暫存器可被用來存盤執行緒內的區域變數,
2)執行緒的生命周期

1.執行緒的創建(三種方式):執行緒被new出來
繼承Thread類,,實作Runnable介面,,實作Callable介面,
2.執行緒就緒:執行緒具有執行的資格,即執行緒呼叫了start(),沒有執行的權利
Thread t = new Thread();
t.start();//執行緒就緒,準備啟動
3執行緒運行:執行緒具有執行的資格和具備執行的權利
@Override
public void run() {
for (int i = 0; i <= 99; i++) {
System.out.println(currentThread().getName() + ">>>" + i);
try {
sleep(99);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
4.執行緒阻塞:沒有執行的資格和執行的權利
sieep()方法:他使得執行緒在指定的時間內進入阻塞狀態,不能得到CPU時間,指定的時間一過,執行緒重新進入可執行狀態
/**
* 阻塞---Sleep()
*
* @param args
*/
public static void main(String[] args) {
Thread a1 = new SleepThread();
Thread a2 = new SleepThread();
Thread a3 = new SleepThread();
Thread a4 = new SleepThread();
a1.start();
a2.start();
a3.start();
a4.start();
}
public static class SleepThread extends Thread {
@Override
public void run() {
for (int i = 0; i <= 18; i++) {
System.out.println(currentThread().getName() + ">>>" + i);
try {
sleep(180); // 休息180ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
yield()方法:使得執行緒放棄當前分得的CPU時間,但是不使執行緒阻塞,即執行緒仍處于可執行狀態
/**
* 阻塞---yield
*
* @author DELL
*
*/
public class ThreadYield {
public static void main(String[] args) {
Thread a1 = new YieldThread();
Thread a2 = new YieldThread();
Thread a3 = new YieldThread();
a1.start();
a2.start();
a3.start();
}
public static class YieldThread extends Thread {
@Override
public void run() {
for (int i = 0; i <= 18; i++) {
if (i == 9) {
Thread.yield();
}
System.out.println(currentThread().getName() + ">>>" + i);
try {
sleep(180);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
join()方法:呼叫時當前執行緒則轉入阻塞狀態,直到另一個執行緒運行結束,當前執行緒再由阻塞狀態轉為就緒狀態
public class ThreadJoin {
public static void main(String[] args) throws Exception {
Thread a1 = new JoinThread();
Thread a2 = new JoinThread();
Thread a3 = new JoinThread();
a1.start();
a1.join(); // 等待a1跑完,再跑其他的
a2.start();
a3.start();
}
public static class JoinThread extends Thread {
@Override
public void run() {
for (int i = 0; i <= 18; i++) {
System.out.println(currentThread().getName() + ">>>" + i);
try {
sleep(180);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
wait()方法:導致當前執行緒等待,直到其他執行緒呼叫此物件的notify()喚醒方法,
5.執行緒死亡:執行緒的物件變成垃圾,釋放資源
二.創建執行緒的三種方式
1)繼承Thread類
創建如下:
public class ThreadDemo01 extends Thread {
/**
* 執行緒創建方法1--繼承Thread類
*/
@Override
public void run() {
for (int i = 0; i <= 99; i++) {
System.out.println(currentThread().getName() + ">>>" + i);
try {
sleep(99);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
測驗如下:
public static void main(String[] args) {
Thread t1 = new ThreadDemo01();
Thread t2 = new ThreadDemo01();
Thread t3 = new ThreadDemo01();
Thread t4 = new ThreadDemo01();
Thread t5 = new ThreadDemo01();
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
2)實作Runnable介面
創建如下:(也可用lambda運算式創建)
public class ThreadDemo02 implements Runnable {
/**
* Thread創建方法二 繼承介面Runable
*/
@Override
public void run() {
for (int i = 0; i <= 99; i++) {
System.out.println(Thread.currentThread().getName() + ">>>" + i);
try {
Thread.sleep(99);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
測驗如下:
public class TestThreadDemo02 {
public static void main(String[] args) {
Thread t1 = new Thread(new ThreadDemo02());
Thread t2 = new Thread(new ThreadDemo02());
Thread t3 = new Thread(new ThreadDemo02());
Thread t4 = new Thread(new ThreadDemo02());
Thread t5 = new Thread(new ThreadDemo02());
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
3)實作Callable介面
創建如下:
public class ThreadDemo03 implements Callable<Integer> {
/**
* Thread 第三種創建方法--繼承與Callable介面----此方法適用于值的計算
*/
@Override
public Integer call() throws Exception {
int t = 0;
for (int i = 0; i < 99; i++) {
t += i;
}
return t;
}
}
測驗如下:
public class TestThreadDemo03 {
public static void main(String[] args) throws Exception {
FutureTask<Integer> ft = new FutureTask<Integer>(new ThreadDemo03());
Thread t = new Thread(ft);
t.start();
System.out.println(ft.get());
}
}
//輸出:4851
4)Callable與其他兩種方式的區別
1.call()方法可以有回傳值
2.call()方法可以宣告拋出例外
3.通常在做計算時使用
代碼展示如下:
@Override
public Integer call() throws Exception { //可以直接拋出例外
int t = 0;
for (int i = 0; i < 99; i++) {
t += i;
}
return t; //回傳值
}
三.CAS原理與ABA問題
1.CAS原理:C–compare;A–And; S–Swap;比較并交換
CAS有三個運算元:記憶體值(V), 預期原值(A), 新值(B);如果記憶體值與預期原值相同時,處理器自動將該位置的值(V)修改為新值(B),否則,處理器不做任何的操作,
偽代碼可以表示為:
do{
備份舊資料;
基于舊資料構造新資料;
}while(!CAS(記憶體地址,備份的舊資料,新資料))

2.ABA問題:CAS需要在操作值的時候檢查下值有沒有發生變化,如果沒有發生變化則更新,但是如果一個值原來是A,變成了B,又變成了A,那么使用CAS進行檢查時會發現它的值沒有發生變化,但是實際上卻變化了;比如鏈表的頭部在變化了兩次后恢復了原值,但是不代表鏈表就沒有變化,,,所以java提供了:AtomicStampedReference/AtomicMarkableReference 來處理會發生ABA問題的場景,主要是在物件中額外在增加一個標記來標識物件是否有變更過,
四.volatile和synchronized關鍵字
1)volatile關鍵字
volatile變數用來確保將變數的更新操作通知到其他執行緒;能在變數級別使用;能夠實作變數修改的可見性,不能保證其原子性;在訪問volatile變數時不會執行加鎖操作,因此也就不會使執行線發生阻塞,,并且用它標記的變數不會被編譯器優化,
public class ThreadSecurity01 {
static volatile boolean flag = false; // 執行緒安全,實作了變數的可見性
public static void main(String[] args) throws Exception {
Thread a = new ThreadA();
Thread b = new ThreadB();
a.start();
Thread.sleep(1000);
b.start();
}
public static class ThreadA extends Thread {
@Override
public void run() {
while (true) {
if (flag) {
System.out.println("A>>" + flag);
try {
Thread.sleep(180);
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
}
}
}
public static class ThreadB extends Thread {
@Override
public void run() {
flag = true;
System.out.println("B>>flag>>" + flag);
try {
Thread.sleep(180);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
2)synchronized關鍵字
本質上時鎖定當前變數,只有當前執行緒可以訪問該變數,其他執行緒被堵塞住,
使用級別:在變數,方法,和類級別的
可以保證變數的修改可見性和原子性
可能會造成執行緒的阻塞
標記的變數可以被編譯器優化
synchronized時java中的關鍵字,是一種同步鎖,它修飾的物件有以下幾種:
1.修飾代碼塊:被修飾的代碼塊稱為同步陳述句塊,范圍是{}內的陳述句,作用的物件是呼叫這個代碼塊的物件,
2.修飾一個方法,被修飾的方法稱為同步方法,范圍是整個方法,作用物件是呼叫這個方法的物件,
3.修飾一個類,作用范圍是synchronized后面括號括起來的部分,作用的物件是這個類的所有物件,
4.修飾一個靜態方法,作用范圍是整個靜態方法,作用的物件是這個類的所有物件,
代碼如下:
public class ThreadSynchronized {
public static void main(String[] args) {
Ticket ticket = new Ticket();
TicketThread a1 = new TicketThread(ticket, "A");
TicketThread a2 = new TicketThread(ticket, "B");
TicketThread a3 = new TicketThread(ticket, "C");
TicketThread a4 = new TicketThread(ticket, "D");
TicketThread a5 = new TicketThread(ticket, "E");
a1.start();
a2.start();
a3.start();
a4.start();
a5.start();
}
/**
* 解決方法一,synchronized鎖定方法
*
* @author DELL
*
*/
// public static class Ticket {
// int num = 100;
//
// synchronized void sold(String name) { // 站點賣票
// System.out.println(name + "賣出一張后,還余多少張:" + (--num));
// }
//
// int getNum() {
// return this.num;
// }
// }
/**
* 方法二:synchronized鎖定陳述句塊--
*
* @author DELL
*
*/
public static class Ticket {
int num = 100;
void sold(String name) {// 站點賣票
synchronized (this) {
System.out.println(name + "賣出一張后,還余多少張:" + (--num));
}
}
int getNum() {
return this.num;
}
}
public static class TicketThread extends Thread {
private Ticket ticket;
private String name;
public TicketThread(Ticket ticket, String name) {
this.ticket = ticket;
this.name = name;
}
@Override
public void run() {
while (ticket.getNum() > 0) {
ticket.sold(this.name);
try {
Thread.sleep(99);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
五.AtomicInteger與int的區別
1.AtomicInteger這個類是為了滿足在高并發的情況下,原生的整數型值自增執行緒不安全問題,int是執行緒不安全的;用AtomicInteger可確保安全,,但是只能保證在自增或者自減的情況下保證執行緒安全
public class ThreadSecurity02 {
// static volatile int x = 0; //此時執行緒不安全,只保證了可見性,++i,i++,,原子性不安全,,,,解決辦法如下
//
// public static void main(String[] args) {
// Thread a1 = new AtomicThread();
// Thread a2 = new AtomicThread();
// Thread a3 = new AtomicThread();
//
// a1.start();
// a2.start();
// a3.start();
//
// }
//
// public static class AtomicThread extends Thread {
// @Override
// public void run() {
// for (int i = 0; i <= 100; i++) {
// System.out.println("X>>" + (++x));
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// }
// }
static volatile AtomicInteger x = new AtomicInteger(0);
public static void main(String[] args) {
Thread a1 = new AtomicThread();
Thread a2 = new AtomicThread();
Thread a3 = new AtomicThread();
a1.start();
a2.start();
a3.start();
}
public static class AtomicThread extends Thread {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println("X>>" + (x.incrementAndGet()));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
六.執行緒的三大特性
程式在運行時,如果不滿足這三大特性,就有可能產生執行緒安全問題,
1)原子性
一個操作或者多個操作要么全部執行并且執行程序中不被任何因素打斷,要么就不執行
/**
* 原子性實體
*
* @author DELL
*
*/
public class ThreadSecurity02 {
// static volatile int x = 0; //++i,i++,,原子性不安全,,,,解決辦法如下
//
// public static void main(String[] args) {
// Thread a1 = new AtomicThread();
// Thread a2 = new AtomicThread();
// Thread a3 = new AtomicThread();
//
// a1.start();
// a2.start();
// a3.start();
//
// }
//
// public static class AtomicThread extends Thread {
// @Override
// public void run() {
// for (int i = 0; i <= 100; i++) {
// System.out.println("X>>" + (++x));
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// }
// }
static volatile AtomicInteger x = new AtomicInteger(0);
public static void main(String[] args) {
Thread a1 = new AtomicThread();
Thread a2 = new AtomicThread();
Thread a3 = new AtomicThread();
a1.start();
a2.start();
a3.start();
}
public static class AtomicThread extends Thread {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println("X>>" + (x.incrementAndGet()));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
型別參考可確保原子性安全,如下:

2)有序性
在java記憶體模型中,允許編譯器和處理器對指令進行重排序,但是排序程序中不會影響到單執行緒程式的執行,卻會影響到多執行緒并發執行的正確性
public class ThreadYouXu {
public static class Vars {
static int i = 0, j = 0, k = 0; // 定義三個變數
}
public static void main(String[] args) {
Vars va = new Vars();
Thread a1 = new Thread(new Runnable() {
@Override
public void run() {
va.i = 1;
System.out.print("i>>" + va.i + "!!!");
}
});
Thread a2 = new Thread(new Runnable() {
@Override
public void run() {
va.j = 1;
System.out.print("j>>" + va.j + "!!!");
}
});
Thread a3 = new Thread(new Runnable() {
@Override
public void run() {
va.k = 1;
System.out.print("k>>" + va.k);
}
});
a1.start();
a2.start();
a3.start();
}
// 輸出第一次結果:j>>1!!!i>>1!!!k>>1
// 輸出第二次結果:j>>1!!!k>>1i>>1!!!
// 輸出第三次結果:i>>1!!!j>>1!!!k>>1
}
3)可見性
當多個執行緒同時訪問一個變數時,一個執行緒修改了這個變數的值,其他執行緒能立即看得到它修改的值
public class ThreadSecurity01 {
static volatile boolean flag = false; // 執行緒安全,實作了變數的可見性
public static void main(String[] args) throws Exception {
Thread a = new ThreadA();
Thread b = new ThreadB();
a.start();
Thread.sleep(1000);
b.start();
}
public static class ThreadA extends Thread {
@Override
public void run() {
while (true) {
if (flag) {
System.out.println("A>>" + flag);
try {
Thread.sleep(180);
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
}
}
}
public static class ThreadB extends Thread {
@Override
public void run() {
flag = true;
System.out.println("B>>flag>>" + flag);
try {
Thread.sleep(180);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
七.死鎖的四個必要條件
代碼實體:
/**
* 死鎖問題
*
* @param args
*/
public static void main(String[] args) {
DeadLock a = new DeadLock("A");
DeadLock b = new DeadLock("B");
DeadLock c = new DeadLock("C");
Thread a1 = new DeadLockThread(a, b, c);
Thread a2 = new DeadLockThread(b, c, a);
Thread a3 = new DeadLockThread(c, a, b);
a1.start();
a2.start();
a3.start();
}
public static class DeadLock {
private String name;
public DeadLock(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
public static class DeadLockThread extends Thread {
private DeadLock a;
private DeadLock b;
private DeadLock c;
public DeadLockThread(DeadLock a, DeadLock b, DeadLock c) {
this.a = a;
this.b = b;
this.c = c;
}
@Override
public void run() {
synchronized (a) {
System.out.println("鎖住:" + a.getName() + "資源成功");
System.out.println("準備鎖住:" + b.getName() + "資源成功");
synchronized (b) {
System.out.println("鎖住:" + b.getName() + "資源成功");
System.out.println("準備鎖住:" + c.getName() + "資源成功");
synchronized (c) {
System.out.println("鎖住:" + c.getName() + "資源成功");
}
System.out.println("釋放:" + c.getName() + "資源成功");
}
System.out.println("釋放:" + b.getName() + "資源成功");
}
System.out.println("釋放:" + a.getName() + "資源成功");
}
}
}
// 輸出:鎖住:A資源成功
// 鎖住:C資源成功
//鎖住:B資源成功
//準備鎖住:A資源成功
//準備鎖住:B資源成功
//準備鎖住:C資源成功
1)互斥
一個資源同時只能被一個執行緒所使用
2)不可搶占
行程以及獲得的資源,沒有外界力量來搶占它,資源自能夠自動放棄
3)請求與保持
行程必須占有資源,再去申請,例如請求第二把鎖的時候,需要保持自身的第一把鎖不被釋放
4)回圈等待
兩個執行緒時,是你等我釋放鎖,我等你釋放鎖,多個執行緒時,是頭尾相接的等待----如同一個環形公路一樣
八.可重入鎖
可重入鎖,也叫遞回鎖,值得是同一執行緒 外層函式獲得鎖之后,內層遞回函式仍然有獲取該鎖的代碼,但不影響, 使用靈活,但必須要有釋放鎖的動作,且手動釋放和開啟鎖,只能適用于代碼塊鎖,
Lock 是一個介面提供無條件、可輪訓的、定時的、可中斷的鎖獲取操作,所有加鎖和解鎖的方法都是顯示的,
ReentrantLock(可重入鎖)是Lock介面的實作類,
代碼實體:
/**
* 可重入鎖
*
* @author DELL
*
*/
public class ReenTrantLockThread {
public static void main(String[] args) {
Ticket ticket = new Ticket();
TicketThread a1 = new TicketThread(ticket, "A");
TicketThread a2 = new TicketThread(ticket, "B");
TicketThread a3 = new TicketThread(ticket, "C");
TicketThread a4 = new TicketThread(ticket, "D");
TicketThread a5 = new TicketThread(ticket, "E");
a1.start();
a2.start();
a3.start();
a4.start();
a5.start();
}
public static class Ticket {
private final ReentrantLock lock = new ReentrantLock(); // 創建
int num = 100;
void sold(String name) { // 站點賣票
lock.lock(); // 表示要開始啟動鎖了
try {
System.out.println(name + "賣出一張后,還余多少張:" + (--num));
} finally {
lock.unlock(); // 處理
}
}
int getNum() {
return this.num;
}
}
public static class TicketThread extends Thread {
private Ticket ticket;
private String name;
public TicketThread(Ticket ticket, String name) {
this.ticket = ticket;
this.name = name;
}
@Override
public void run() {
while (ticket.getNum() > 0) {
ticket.sold(this.name);
try {
Thread.sleep(99);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/286899.html
標籤:其他
上一篇:大型企業多賬號管理“安全心法”
