停止線程
停止線程:只要線程停了就叫做停止線程
使用boolean標記方法中斷線程:
class StopRunnable implements Runnable{
public boolean isOver = false;
@Override
public void run() {
while (!isOver) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
}
public class Demo01 {
public static void main(String[] args) throws InterruptedException {
StopRunnable sr = new StopRunnable();
Thread t1 = new Thread(sr);
t1.start();
Thread.sleep(3000);
sr.isOver = true;
System.out.println("停止線程");
Thread.sleep(1000);
System.out.println("主線程結束");
}
}
測試interrupt中斷線程:
public class Demo {
public static void main(String[] args) throws InterruptedException {
StopRunnable sr = new StopRunnable();
Thread t1 = new Thread(sr);
t1.start();
Thread.sleep(3000);
t1.interrupt();
System.out.println("停止線程");
Thread.sleep(1000);
System.out.println("主線程結束");
}
}
class StopRunnable implements Runnable{
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
}
測試結果:
實際上interrupt()這個方法設置了isInterrupted布爾值,如果不測試線程是否中斷,子線程不會中斷
如果線程中有wait()等待或者sleep()休眠
這時調用interrupt方法會拋出一個異常InterruptedException並且清除中斷狀態,子程序繼續運行;
如果線程中沒有等待或者休眠
這時調用interrupt方法會設置中斷狀態(也就是true/false的改變)
測試中斷狀態
public class Demo {
public static void main(String[] args) {
InterruptThread t1 = new InterruptThread();
InterruptThread t2 = new InterruptThread();
t1.start();
t2.start();
for (int i = 0; i < 50; i++) {
if (i == 25) {
t1.isOver = true;
break;
}
System.out.println(i + "----");
}
System.out.println("主線程結束");
}
}
class InterruptThread extends Thread{
public boolean isOver = false;
@Override
public synchronized void run() {
while (!isOver) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "run");
}
}
}
測試結果:線程1進來帶着鎖遇到wait方法放棄了CPU的執行資源,但是鎖會還回去
緊接着線程2拿着鎖進來,又遇到wait方法,也放棄了CPU的執行資源,相當於兩個線程進入冷凍(中斷)狀態
注意:如果在非同步方法裏調用wait方法,會出現IllegalMonitorStateException異常(對象監視器就是對象鎖)
所以在調用wait方法必須擁有對象鎖
public class Demo {
public static void main(String[] args) {
InterruptThread t1 = new InterruptThread();
InterruptThread t2 = new InterruptThread();
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 InterruptThread extends Thread{
@Override
public synchronized void run() {
while (true) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "run");
}
}
}
測試結果:
調用interrupt方法可以消除該狀態,但是碰到wait方法會拋出異常,所以建議進來不要使用interrupt方法
如果要停止線程直接使用標記法來停止,只有遇到了等待狀態時,可以使用該方法強行清除該等待狀態
間隔輸出
需求:
Person類 姓名 性別
開啓兩個線程
一個對Person對象進行賦值
一個對Person對象進行打印
一次打印 張三 男
一次打印 zahngsan nv
間隔輸出
分析:
1.先保證要操作都是同一個對象
2.要保證數據安全需要使用鎖並且要使用同一把鎖
3.保證操作的邏輯順序要對(先賦值 再打印)用 wait 和 notify
class Person {
public String name;
public String gender;
public boolean flag = false;
}
class SetRunnable implements Runnable{
private Person p;
private boolean isTrue = true;
public SetRunnable() {
super();
}
public SetRunnable(Person p) {
super();
this.p = p;
}
@Override
public void run() {
while (true) {
synchronized (p) {
if (p.flag == true) {
try {
p.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (isTrue) {
p.name = "張三";
p.gender = "男";
} else {
p.name = "zhangsan";
p.gender = "nv";
}
isTrue = !isTrue;
p.flag = true;
p.notify();
}
}
}
}
class PrintRunnable implements Runnable{
private Person p;
public PrintRunnable() {
super();
}
public PrintRunnable(Person p) {
super();
this.p = p;
}
@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.gender);
p.flag = false;
p.notify();
}
}
}
}
public class Demo06 {
public static void main(String[] args) {
Person person = new Person();
SetRunnable sr = new SetRunnable(person);
Thread t1 = new Thread(sr);
t1.start();
PrintRunnable pr = new PrintRunnable(person);
Thread t2 = new Thread(pr);
t2.start();
}
}
間隔輸出邏輯:
賦值線程需要賦值完畢,打印線程才能去打印
打印線程需要打印完畢,賦值線程才能去賦值
賦值的時候打印線程在等待,等賦值完成後才能去打印打印完成後通知賦值線程繼續賦值
實現間隔輸出:
使用一個標記完成切換wait() 和 notify(),實現上面的邏輯
兩個線程必須保證使用同一個標記,所以標記要聲明在Person類中
代碼優化:把之前上鎖的代碼封裝到類中,從同步代碼塊封裝成同步方法
修改部分代碼如下(未改同上):
class Person {
public synchronized void setPerson(String name, String gender) {
if (flag == true) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
this.gender = gender;
flag = true;
this.notify();
}
public synchronized void printPerson() {
if (flag == false) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name + "---" + gender);
flag = false;
this.notify();
}
}
class PrintRunnable implements Runnable{
@Override
public void run() {
while(true) {
p.printPerson();
}
}
}
class SetRunnable implements Runnable{
@Override
public void run() {
while (true) {
if (isTrue) {
p.setPerson("張三", "男");
} else {
p.setPerson("zhangsan", "nv");
}
isTrue = !isTrue;
}
}
}