基本步驟
優點 :
缺點 :
基本步驟
優點 :
缺點 :
基本步驟
注意事項
優點 :
缺點 :
同步程式碼塊 : 鎖住多條語句操作共用資料,可以使用同步程式碼塊實現
第一部分 : 格式
synchronized(任意物件) {
多條語句操作共用資料的程式碼
}
第二部分 : 注意
1 預設情況鎖是開啟的,只要有一個執行緒進去執行程式碼了,鎖就會關閉
2 當執行緒執行完出來了,鎖才會自動開啟
第三部分 : 同步的好處和弊端
好處 : 解決了多執行緒的資料安全問題
弊端 : 當執行緒很多時,因為每個執行緒都會去判斷同步上的鎖,這是很耗費資源的,無形中會降低程式的執行效率
同步方法:就是把synchronized關鍵字加到方法上
格式:修飾符 synchronized 返回值型別 方法名(方法引數) { }
同步程式碼塊和同步方法的區別:
1 同步程式碼塊可以鎖住指定程式碼,同步方法是鎖住方法中所有程式碼
2 同步程式碼塊可以指定鎖物件,同步方法不能指定鎖物件
注意 : 同步方法時不能指定鎖物件的 , 但是有預設存在的鎖物件的。
1 對於非static方法,同步鎖就是this。
2 對於static方法,我們使用當前方法所在類的位元組碼物件(類名.class)。 Class型別的物件
雖然我們可以理解同步程式碼塊和同步方法的鎖物件問題,但是我們並沒有直接看到在哪裡加上了鎖,在哪裡釋放了鎖,
為了更清晰的表達如何加鎖和釋放鎖,JDK5以後提供了一個新的鎖物件Lock
Lock中提供了獲得鎖和釋放鎖的方法
void lock():獲得鎖
void unlock():釋放鎖
Lock是介面不能直接範例化,這裡採用它的實現類ReentrantLock來範例化
ReentrantLock的構造方法
ReentrantLock():建立一個ReentrantLock的範例
注意:多個執行緒使用相同的Lock鎖物件,需要多執行緒運算元據的程式碼放在lock()和unLock()方法之間。一定要確保unlock最後能夠呼叫
public class DeadLockDemo {
public static void main(String[] args) {
String 筷子A = "筷子A";
String 筷子B = "筷子B";
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (筷子A) {
System.out.println("小白拿到了筷子A ,等待筷子B....");
synchronized (筷子B) {
System.out.println("小白拿到了筷子A和筷子B , 開吃!!!!!");
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "小白").start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (筷子B) {
System.out.println("小黑拿到了筷子B ,等待筷子A....");
synchronized (筷子A) {
System.out.println("小黑拿到了筷子B和筷子A , 開吃!!!!!");
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "小黑").start();
}
}
執行緒間的通訊技術就是通過等待和喚醒機制,來實現多個執行緒協同操作完成某一項任務,例如經典的生產者和消費者案例。等待喚醒機制其實就是讓執行緒進入等待狀態或者讓執行緒從等待狀態中喚醒,需要用到兩種方法,如下:
等待方法 :
喚醒方法 :
注意
package com.itcast.waitnotify_demo2;
import sun.security.krb5.internal.crypto.Des;
/*
生產者步驟:
1,判斷桌子上是否有漢堡包
如果有就等待,如果沒有才生產。
2,把漢堡包放在桌子上。
3,叫醒等待的消費者開吃
*/
public class Cooker implements Runnable {
@Override
public void run() {
while (true) {
synchronized (Desk.lock) {
if (Desk.count == 0) {
break;
} else {
if (Desk.flag) {
// 桌子上有食物
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
// 桌子上沒有食物
System.out.println("廚師生產了一個漢堡包...");
Desk.flag = true;
Desk.lock.notify();
}
}
}
}
}
}
package com.itcast.waitnotify_demo2;
import sun.security.krb5.internal.crypto.Des;
/*
消費者步驟:
1,判斷桌子上是否有漢堡包。
2,如果沒有就等待。
3,如果有就開吃
4,吃完之後,桌子上的漢堡包就沒有了
叫醒等待的生產者繼續生產
漢堡包的總數量減一
*/
public class Foodie implements Runnable {
@Override
public void run() {
while (true) {
synchronized (Desk.lock) {
if (Desk.count == 0) {
break;
} else {
if (Desk.flag) {
// 桌子上有食物
System.out.println("吃貨吃了一個漢堡包...");
Desk.count--; // 漢堡包的數量減少一個
Desk.flag = false;// 桌子上的食物被吃掉 , 值為false
Desk.lock.notify();
} else {
// 桌子上沒有食物
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
package com.itcast.waitnotify_demo2;
public class Test {
public static void main(String[] args) {
new Thread(new Foodie()).start();
new Thread(new Cooker()).start();
}
}
package com.itcast.threadpool_demo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/*
1 需求 :
使用執行緒池模擬游泳教練教學生游泳。
游泳館(執行緒池)內有3名教練(執行緒)
游泳館招收了5名學員學習游泳(任務)。
2 實現步驟:
建立執行緒池指定3個執行緒
定義學員類實現Runnable,
建立學員物件給執行緒池
*/
public class Test1 {
public static void main(String[] args) {
// 建立指定執行緒的執行緒池
ExecutorService threadPool = Executors.newFixedThreadPool(3);
// 提交任務
threadPool.submit(new Student("小花"));
threadPool.submit(new Student("小紅"));
threadPool.submit(new Student("小明"));
threadPool.submit(new Student("小亮"));
threadPool.submit(new Student("小白"));
threadPool.shutdown();// 關閉執行緒池
}
}
class Student implements Runnable {
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void run() {
String coach = Thread.currentThread().getName();
System.out.println(coach + "正在教" + name + "游泳...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(coach + "教" + name + "游泳完畢.");
}
}
package com.itcast.threadpool_demo;
import java.util.concurrent.*;
/*
需求: Callable任務處理使用步驟
1 建立執行緒池
2 定義Callable任務
3 建立Callable任務,提交任務給執行緒池
4 獲取執行結果
<T> Future<T> submit(Callable<T> task) : 提交Callable任務方法
返回值型別Future的作用就是為了獲取任務執行的結果。
Future是一個介面,裡面存在一個get方法用來獲取值
練一練:使用執行緒池計算 從0~n的和,並將結果返回
*/
public class Test2 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 建立指定執行緒數量的執行緒池
ExecutorService threadPool = Executors.newFixedThreadPool(10);
Future<Integer> future = threadPool.submit(new CalculateTask(100));
Integer sum = future.get();
System.out.println(sum);
}
}
// 使用執行緒池計算 從0~n的和,並將結果返回
class CalculateTask implements Callable<Integer> {
private int num;
public CalculateTask(int num) {
this.num = num;
}
@Override
public Integer call() throws Exception {
int sum = 0;// 求和變數
for (int i = 0; i <= num; i++) {
sum += i;
}
return sum;
}
}