Java -- 執行緒( Thread 類 和 Runnable介面 ) , 執行緒安全 .
一、執行緒
執行緒: 程序內部一個獨立執行單元(通向CPU的一條路徑.)
1. Thread類:
常用方法:
Thread.currentThread().getName(); 獲取當前執行緒的名稱.
Thread.sleep(long millis): 讓當前執行緒睡眠多少毫秒,之後繼續執行執行緒。
使用繼承類的方式建立執行緒:
a.建立一個Thread的子類 , 繼承Thread類.
b.重寫Thread類的run方法 , 設定執行緒要執行的任務.
c.建立Thread的子類物件.
d.呼叫Start方法 , - - - - > 開啟新執行緒 .
// 1. 建立一個子類 , 繼承Thread類 .
public class MyThread extends Thread{
// 2.重寫run方法
@Override
public void run() {
// 設定執行緒任務 -- 獲取當前正在執行的執行緒的名稱
System.out.println(Thread.currentThread().getName());
}
}
// 測試類
public class Demo01GetThreadName {
public static void main(String[] args) {
System.out.println("這裡是main執行緒." );
// 3. 建立子類物件 MyThread
MyThread mt = new MyThread();
// 4. 呼叫start方法, 開啟執行緒
mt.start();
// 獲取主執行緒的名稱.
String name = MyThread.currentThread().getName();
System.out.println(name);
}
}
2. Runnable介面(重點)
使用Runnable介面的好處:
a.避免了實現類單繼承的侷限性(還可以繼承其他類, 實現其他介面.)
b.降低了程式的耦合性,提高了程式的擴充套件性.
實現方式:
1.建立一個類實現Runnable介面.
2.重寫介面中的run方法 , 設定執行緒任務.
3.建立介面的實現類物件 .
4.建立Thread類物件 , 在構造方法中傳遞Runnable介面的實現類物件
5.呼叫Threa類中start開啟新執行緒.
// 1. 建立一個子類 , 實現 Runnable 介面 .
public class RunnableImpl implements Runnable {
// 2. 重寫run方法 , 設定執行緒任務.
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
// 測試類
public class Demo01Runnable{
public static void main(String[] args) {
// 3.建立介面的實現類物件 .
RunnableImpl r = new RunnableImpl();
// 4.建立Thread類物件 . 介面的實現類物件作為引數
Thread t = new Thread(r);
// 5.開啟新執行緒
t.start();
// 3 , 4 , 5的簡寫步驟 .
new Thread(new RunnableImpl2()).start();
// 主執行緒開啟完新的執行緒,會繼續執行
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
3. 匿名內部類:
匿名內部類方式開啟多執行緒:
匿名內部類作用: 簡化程式碼
格式:
new 父類/介面( ){
重寫父類/介面中的方法
};
// 匿名內部類的寫法:
public class Demo01Thread {
public static void main(String[] args) {
// Runnable介面的匿名內部類
new Thread(new Runnable(){
@Override
public void run() { // 重寫run方法 , 設定執行緒任務.
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"----"+i);
}
}
}).start(); // 開啟執行緒.
// 主執行緒
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
二、執行緒安全
出現執行緒安全的問題:
多執行緒訪問了同一個共享的資料。
使用執行緒安全:
a.增加了程式的安全性.
b.降低了效率.
1. 同步程式碼塊
synchronized(鎖物件){
可能出現安全問題的程式碼
(訪問了共享資料的程式碼)
}
注意:
1.鎖物件,可以是任意的物件
2.必須保證多個執行緒使用的是同一個鎖物件
/*
賣票案例出現了執行緒安全問題:出現了重複的票和不存在的票
解決執行緒安全問題的第一種方式:使用同步程式碼塊
*/
// 建立子類 , 實現Runnnabl介面
public class RunnableImpl implements Runnable {
// 定義一個共享的票
private int ticket = 100;
// 重寫run方法 .
@Override
public void run() {
// 使用同步程式碼塊 . 保證執行緒的安全 .
synchronized (this){
while (true) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "正在賣第" + ticket + "張票!");
ticket--;
}
}
}
}
}
2.同步方法
同步鎖是誰?
同步方法中 : 同步鎖就是 t h i s(誰呼叫 , 誰就是this)
靜態同步方法中: 當前方法所在類的位元組碼物件(類名.class)。
// 1 . 建立一個類 , 實現runnable介面
public class RunnableImpl2 implements Runnable {
// 建立一個共享的票
private static int ticket = 100;
// 重寫run方法 ,
@Override
public void run() {
// 執行緒任務 - 賣票
while (true){
getTicket();
}
}
/*
定義一個賣票的方法
1.定義一個方法,新增一個同步synchronized修飾符
同步方法的鎖物件使用的是this
哪個物件呼叫的方法,方法中的this就是哪個物件,本類的物件
this就是Runnable的實現類物件RunnableImpl
*/
public synchronized void getTicket() {
//2.把訪問了共享資料的程式碼,放到同步方法中
// synchronized (this) {
if (ticket > 0) {
//為了提高安全問題出現的機率,讓程式睡眠10毫秒
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在賣第" + ticket + "張票!");
ticket--;
}
//}
}
}
3.Lock鎖
介面中的方法:
void lock() 獲取鎖。
void unlock() 釋放鎖。
同步鎖使用的弊端:
當執行緒任務中出現了多個同步(多個鎖)時,如果同步中嵌套了其他的同步。這時容易引發一種現象:程式出現無限等待,這種現象我們稱為死鎖。
public class RunnableImpl3 implements Runnable {
private int ticket= 100;
// 1. 在成員位置建立一個Lock介面的實現類物件ReentrantLock
Lock r = new ReentrantLock();
@Override
public void run() {
while (true){
r.lock(); // 2.在可能會出現執行緒安全問題的程式碼前呼叫lock方法獲取鎖物件
if (ticket > 0) {
//為了提高安全問題出現的機率,讓程式睡眠10毫秒
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在賣第" + ticket + "張票!");
ticket--;
}
r.unlock(); // 3.在可能會出現執行緒安全問題的程式碼後呼叫unlock方法釋放鎖物件
}
}
}
4.等待(wait)與喚醒(notify)
a. 必須使用鎖物件呼叫 .
b. wait和notify方法一般使用在同步中