線程中斷
測試 interrupt() 方法是否能中斷線程:
// 測試中斷線程
public class Test{
public static void main(String[] args){
TestRunnable runnable = new TestRunnable();
Thread thread = new Thread(runnable);
thread.start();
// 調用 interrupt() 方法
thread.interrupt();
}
}
class TestRunnable implements Runnable{
@Override
public void run(){
int num = 0;
// 測試線程是否中斷 初始線程啓動 爲false 調用 interrupt方法 變成爲true
while(!Thread.currentThread().isInterrupted()){
try{
// 讓線程休眠
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("線程運行" + num++);
}
}
}
/*
* 從輸出情況來看 我們發現 線程循環並沒有出現中斷
* 而且還拋出了 InterruptedException 異常
* 通過查詢相關文檔我們發現 interrupt 方法並非讓線程中斷
* 而是設置線程 isInterrupt() 布爾值 如果線程中斷 返回true 反之爲false
* 那麼 拋出異常是什麼回事呢?
* 我們發現當線程中有 wait()和 sleep()方法 在調用interrupt方法就會拋出這個異常
* 那麼我們應該如何才能讓線程停止呢?
*/
修改版本(僞代碼):
// 這裏我們不再使用 interrupt 方法 改用標記法
// 1. 首先初始化一個布爾變量 賦值爲false
// 2. 在將它放入run() 中的循環條件上
// 3. 在主函數中設置停止線程
public class Test{
public static void main(String[] args){
TestRunnable runnable = new TestRunnable();
Thread thread = new Thread(runnable);
thread.start();
runnable.flag = true;
}
}
class TestRunnable implements Runnable{
boolean flag = false;
@Override
public void run(){
int num = 0;
while(!flag){
// do something
}
}
}
上面代碼中我們遇到了 線程中有sleep()方法 在調用 Interrupt() 會拋出異常
現在我們研究一下這個異常到底是什麼
public class Demo03 {
public static void main(String[] args) {
InterruptRunnable t1 = new InterruptRunnable();
InterruptRunnable t2 = new InterruptRunnable();
t1.start();
t2.start();
for (int i = 0; i < 50; i++) {
if (i == 25) {
// 調用中斷方法 來清除狀態
t1.interrupt();
t2.interrupt();
break;
}
System.out.println(i + "----");
}
System.out.println("主線程結束");
}
}
class InterruptRunnable extends Thread{
@Override
public synchronized void run() {
while (true) {
try {
/*
* 線程一 進入 遇到 wait()方法
* 放棄 CPU 的執行權 但是鎖會還回去
* 線程二 進入 同理
* 相當於兩個線程都進入了冷凍(中斷)狀態
* 解決冷凍狀態
* 調用 interrupt() 清除該狀態
*/
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---" + "run..");
}
}
}
// 之前已經測試出 有sleep()方法 在調用 Interrupt() 會拋出異常
// InterruptedException
// 這個是當線程在活動之前或活動期間處於正在等待、休眠或佔用狀態且該線程被中斷時,拋出該異常
// 因此我們不推薦使用 interruot() 方法來結束線程 推薦使用標記法
// 當我們開始測試 wait() 時 發現拋出 IllegalMonitorStateException 異常
// 這是個對象監視器 在 沒有同步鎖的時候會拋出 因爲 wait() 是讓調用它的線程進行等待
// 必須使用對象鎖
線程等待與喚醒
代碼示例:
/*
* Person類 姓名 性別
* 開啓兩個線程
* 一個對Person對象進行賦值
* 一個對Person對象進行打印
* 要求
* 一次打印 james man
* 一次打印 JAMES MAN
* 間隔輸出
*/
public class Test{
public static void main(String[] args){
// 線程的創建
Persons person = new Persons();
// 這裏要保證操作的數據是同一個 使用構造方法即可!
SetRunnerable set = new SetRunnerable(person);
PrintRunnable print = new PrintRunnable(person);
Thread setPerson = new Thread(set);
Thread printPerson = new Thread(print);
setPerson.start();
printPerson.start();
}
}
class SetRunnerable implements Runnable{
private boolean sigin = true;
// 定義一個成語變量在構造方法中接收
private Persons p ;
public SetRunnerable(){}
public SetRunnerable(Persons person){
this.p = person;
}
@Override
public void run(){
while(true){
// 同步鎖要求鎖的唯一性 因此使用兩個線程共同使用的對象鎖
synchronized(p){
// 爲滿足間隔輸出
// 在person類中定義一個變量 在這裏進行判斷
// 先不進行線程等待 直接賦值
if (p.flag == true) {
try{
p.wait();
}catch(InterruptedException e){
e.printStackTrace();
}}
// 控制 sigin 賦值兩個變量
if (sigin) {
p.name = "james";
p.sex = "man";
}else{
p.name = "JAMES";
p.sex = "MAN";
}
// 賦值完成後 進行反轉 進行下一輪賦值
sigin = !sigin;
// 在進行賦值結束後 應該進行打印 因此讓本線程進行等待
// 當另一個線程打印結束返回 false 纔會進行下次賦值
p.flag = true;
// 喚醒等待中的線程
p.notify();
}
}
}
}
class PrintRunnable implements Runnable{
private Persons p ;
public PrintRunnable(){}
public PrintRunnable(Persons person){
this.p = person;
}
@Override
public void run(){
while(true){
synchronized(p){
if (p.flag == false) {
try{
p.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println(p.name + "---" + p.sex);
p.flag = false;
p.notify();
}
}
}
}
class Persons{
public String name;
public String sex;
boolean flag = false;
}
單例
瞭解線程鎖後, 我們重新審視了一下單例的懶漢式 假如 有兩個線程同時訪問我們都的對象會不會出現創建兩個對象呢?
答案顯然是會的 這樣很明顯就與我們創建懶漢式的初衷相悖了 在引入對象鎖後 單例 懶漢式就比較安全了
// 單例 懶漢式
public class Test{
// do somthing
}
class Sigin {
Sigin sigin = null;
public static synchronized Sigin getInstance(){
if (sigin == null) {
sigin = new Sigin();
}
return sigin;
}
}