線程間的通信
線程間通信:多個(gè)線程在處理同一資源,但是任務(wù)卻不同
1 多線程執(zhí)行同一資源:
// 資源
class Resource {
String name;
String sex;
}
// 輸入
class Input implements Runnable {
Resource r;
Input(Resource r) {
this.r = r;
}
public void run() {
boolean b = true;
while (true) {
synchronized (Resource.class) {
if (b) {
r.name = "zimo";
r.sex = "man";
} else {
r.name = "子陌";
r.sex = "女";
}
}
b = !(b);
}
}
}
// 輸出
class Output implements Runnable {
Resource r;
Output(Resource r) {
this.r = r;
}
public void run() {
while (true) {
synchronized (Resource.class) {
System.out.println(r.name + ":" + r.sex);
}
}
}
}
class ResourceDemo {
public static void main(String[] args) {
// 創(chuàng)建資源
Resource r = new Resource();
// 創(chuàng)建任務(wù)
Input in = new Input(r);
Output out = new Output(r);
// 創(chuàng)建線程
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
// 開(kāi)啟線程
t1.start();
t2.start();
}
}
2 多線程執(zhí)行同一資源 - 加入等待喚醒功能:
- wait()棵癣、notify()、notifyAll(),用來(lái)操作線程為什么定義在了Object類(lèi)中最盅?
- 這些方法存在于同步中
- 使用這些方法時(shí)必須要表示所屬的同步的鎖(對(duì)象監(jiān)視器)
- 鎖可以是任意對(duì)象拨齐,所以任意對(duì)象調(diào)用的方法一定定義Object類(lèi)中
- wait()触机、sleep()有什么區(qū)別跪者?
- wait():釋放cpu執(zhí)行權(quán),釋放鎖
- sleep():釋放cpu執(zhí)行權(quán)妹卿,不釋放鎖
/*
* 涉及的方法:
* 1.wait(); 讓線程處于凍結(jié)狀態(tài)葬荷,被wait的線程會(huì)被存儲(chǔ)到線程池中。
* 2.notify(); 喚醒線程池中的一個(gè)線程(任意的纽帖,無(wú)序的)宠漩。
* 2.notifyAll(); 喚醒線程池中的所有線程。
*/
// 資源
class Resource {
String name;
String sex;
boolean flag = false;
}
// 輸入
class Input implements Runnable {
Resource r;
Input(Resource r) {
this.r = r;
}
public void run() {
boolean b = true;
while (true) {
synchronized (Resource.class) {
if(r.flag){
// 如果有內(nèi)容懊直,那么寫(xiě)線程等待
r.wait();
}else{
// 如果沒(méi)有內(nèi)容扒吁,就往里寫(xiě)
if (b) {
r.name = "zimo";
r.sex = "man";
} else {
r.name = "子陌";
r.sex = "女";
}
// 標(biāo)記修改
r.flag = true;
// 喚醒輸出線程
r.notify();
b = !(b);
}
}
}
}
}
// 輸出
class Output implements Runnable {
Resource r;
Output(Resource r) {
this.r = r;
}
public void run() {
while (true) {
synchronized (Resource.class) {
if(!r.flag){
r.wait();
}else{
System.out.println(r.name + ":" + r.sex);
r.flag = false;
r.notify();
}
}
}
}
}
class ResourceDemo {
public static void main(String[] args) {
// 創(chuàng)建資源
Resource r = new Resource();
// 創(chuàng)建任務(wù)
Input in = new Input(r);
Output out = new Output(r);
// 創(chuàng)建線程
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
// 開(kāi)啟線程
t1.start();
t2.start();
}
}
代碼優(yōu)化:
// 資源
class Resource {
private String name;
private String sex;
private boolean flag = false;
public synchronized void set(String name, String sex){
if(flag){
try{
this.wait();
}catch(InterruptedException e){
}
}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
public synchronized void out(){
if(!flag){
try{
this.wait();
}catch(InterruptedException e){
}
}
System.out.println("name :" + name + ",sex :" + sex);
flag = false;
this.notify();
}
}
// 輸入
class Input implements Runnable {
Resource r;
Input(Resource r) {
this.r = r;
}
public void run() {
boolean b = true;
while (true) {
if (b) {
r.set("zimo", "man");
} else {
r.set("子陌", "女");
}
b = !(b);
}
}
}
// 輸出
class Output implements Runnable {
Resource r;
Output(Resource r) {
this.r = r;
}
public void run() {
while (true) {
r.out();
}
}
}
class ResourceDemo {
public static void main(String[] args) {
// 創(chuàng)建資源
Resource r = new Resource();
// 創(chuàng)建任務(wù)線程
Thread t1 = new Thread(new Input(r));
Thread t2 = new Thread(new Output(r));
// 開(kāi)啟線程
t1.start();
t2.start();
}
}
3 多生產(chǎn)者-多消費(fèi)者模式:
// 多生產(chǎn)者-多消費(fèi)者
// 產(chǎn)品資源
class Resource{
private String name;
private int count;
private boolean flag = false;
public synchronized void set(String name){
while(flag){
try{this.wait();}catch(InterruptedException e){}
}
this.name = name + this.count;
count++;
System.out.println(Thread.currentThread().getName() + "..生產(chǎn)者.." + this.name);
flag = true;
notifyAll();
}
public synchronized void out(){
while(!flag){
try{this.wait();}catch(InterruptedException e){}
}
System.out.println(Thread.currentThread().getName() + "..消費(fèi)者.." + this.name);
flag = false;
notifyAll();
}
}
// 生產(chǎn)者
class Producer implements Runnable{
private Resource r;
Producer(Resource r){
this.r = r;
}
public void run(){
while(true){
r.set("烤鴨");
}
}
}
// 消費(fèi)者
class Consumer implements Runnable{
private Resource r;
Consumer(Resource r){
this.r = r;
}
public void run(){
while(true){
r.out();
}
}
}
class ProducerConsumerDemo{
public static void main(String[] args){
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
- if標(biāo)記:只有一次判斷,會(huì)導(dǎo)致不該運(yùn)行的線程運(yùn)行了室囊,出現(xiàn)數(shù)據(jù)錯(cuò)誤的情況
- while判斷標(biāo)記:解決了線程重新喚醒執(zhí)行權(quán)后雕崩,是否能繼續(xù)往下運(yùn)行!如果本方喚醒了本方融撞,沒(méi)有意義盼铁,notify + while 會(huì)導(dǎo)致死鎖
- notifyAll:解決了,本方線程一定會(huì)喚醒對(duì)方線程的問(wèn)題
JDK升級(jí)1.5 - 同時(shí)更名為5.0版本尝偎,同理1.6為6.0
notify()不僅喚醒了對(duì)方饶火,同時(shí)喚醒了所有人鹏控,所以5.0推出java.util.concurrent.licks
同步代碼塊synchronized,對(duì)于鎖的操作是隱士的肤寝,jdk1.5后將同步鎖封裝成了對(duì)象当辐,并將鎖操作的隱式方式定義到了該對(duì)象中,將隱式動(dòng)作變成了顯示動(dòng)作
- Lock替代了Condition方法的使用
- Condition替代了Object監(jiān)視器方法的使用
class LockDemo{
// 創(chuàng)建一個(gè)鎖對(duì)象
Lock lock = new ReentrantLock();
// 通過(guò)已有的鎖 獲取該鎖上的監(jiān)視器對(duì)象
Condition c = lock.newCondition();
//public synchronized void out(){
public void out(){
lock.lock(); /// 獲取鎖
try{
while(!flag){
//try{this.wait();}catch(InterruptedException e){}
try{c.await();}catch(InterruptedException e){}
}
System.out.println(Thread.currentThread().getName() + "..消費(fèi)者.." + this.name);
flag = false;
c.signalAll();
}finally{
lock.unlock();
}
}
// 通過(guò)已有的鎖獲取兩組監(jiān)視器:一組監(jiān)視生產(chǎn)者鲤看,一組監(jiān)視消費(fèi)者
// 生產(chǎn)的時(shí)候消費(fèi)不能動(dòng)
// 創(chuàng)建一個(gè)鎖對(duì)象
Lock lock = new ReentrantLock();
// 通過(guò)已有的鎖 獲取該鎖上的監(jiān)視器對(duì)象
Condition producer_con = lock.newCondition(); // 生產(chǎn)者監(jiān)視器
Condition consumer_con = lock.newCondition(); // 消費(fèi)者監(jiān)視器
public synchronized void set(String name){
while(flag){
try{producer_con.wait();}catch(InterruptedException e){}
}
this.name = name + this.count;
count++;
System.out.println(Thread.currentThread().getName() + "..生產(chǎn)者.." + this.name);
flag = true;
consumer_con.signal();
}
public void out(){
lock.lock(); /// 獲取鎖
try{
while(!flag){
//try{this.wait();}catch(InterruptedException e){}
try{consumer_con.await();}catch(InterruptedException e){}
}
System.out.println(Thread.currentThread().getName() + "..消費(fèi)者.." + this.name);
flag = false;
producer_con.signal();
}finally{
lock.unlock();
}
}
}
- Lock接口:替代了同步代碼塊或者同步函數(shù)缘揪。將同步的隱式鎖操作變成顯示鎖操作。同時(shí)更為靈活义桂,可以一個(gè)鎖上加上多組監(jiān)視器
- lock():獲取鎖
- unlock():釋放鎖找筝,通常需要定義fianlly代碼塊中
- Condition接口:替代了Object中的wait、notify慷吊、notifyAll方法袖裕,將這些監(jiān)視器方法單獨(dú)進(jìn)行了封裝,變成了Condition監(jiān)視器對(duì)象罢浇,可任意鎖進(jìn)行組合
- await():等價(jià)于wait()
- signal():等價(jià)于notify()
- signalAll():等價(jià)于notifyAll()
4陆赋、wait()和sleep()的區(qū)別
-
wait可以指定時(shí)間沐祷,也可以不指定時(shí)間
sleep必須指定時(shí)間
-
在同步中時(shí)嚷闭,對(duì)于cpu的執(zhí)行權(quán)和鎖的處理不同
- wait:釋放執(zhí)行權(quán),釋放鎖
- sleep:釋放執(zhí)行權(quán)赖临,不釋放鎖
停止線程
- stop()方法
- run()方法結(jié)束
1) 定義循環(huán)結(jié)束標(biāo)記
- 因?yàn)榫€程運(yùn)行代碼一般都是循環(huán)胞锰,只要控制了循環(huán)即可
class StopThread implements Runnable{
private boolean flag = true;
public void run(){
while(flag){
System.out.println(Thread.currentThread().getName() + ".....");
}
}
public void setFlag(){
this.flag = false;
}
}
class StopThreadDemo{
public static void main(String[] args){
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int num = 1;
for(;;){
if(++num == 50){
st.setFlag();
break;
}
System.out.println("main...." + num);
}
}
}
如果線程處于凍結(jié)狀態(tài),無(wú)法讀取標(biāo)記兢榨,如何結(jié)束嗅榕?
2)使用interrupt(中斷)方法
- 該方法是結(jié)束線程的凍結(jié)狀態(tài),使線程(強(qiáng)制)回到運(yùn)行狀態(tài)中來(lái)
- 強(qiáng)制動(dòng)作會(huì)發(fā)生InterruptException異常吵聪,記得要處理
class StopThread implements Runnable{
private boolean flag = true;
public synchronized void run(){
while(flag){
try{
wait(); // 當(dāng)主線程死亡凌那,子線程沒(méi)有結(jié)束進(jìn)入等待
}catch(InterruptedException e){
System.out.println(Thread.currentThread().getName() + "......" + e);
flag = false;
}
System.out.println(Thread.currentThread().getName() + "*******");
}
}
public void setFlag(){
this.flag = false;
}
}
class StopThreadDemo{
public static void main(String[] args){
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int num = 1;
for(;;){
if(++num == 50){
//st.setFlag(); // 線程進(jìn)入凍結(jié),無(wú)法讀取標(biāo)記
t1.interrupt();
t2.interrupt();
break;
}
System.out.println("main...." + num);
}
System.out.println("main....end");
}
}
3)守護(hù)線程(聯(lián)合線程/后臺(tái)線程)
- 如果前臺(tái)線程都結(jié)束了吟逝,守護(hù)線程(后臺(tái)線程)無(wú)論處于什么狀態(tài)都將自動(dòng)結(jié)束
class StopThread implements Runnable{
private boolean flag = true;
public synchronized void run(){
while(flag){
try{
wait(); // 當(dāng)主線程死亡帽蝶,子線程沒(méi)有結(jié)束進(jìn)入等待
}catch(InterruptedException e){
System.out.println(Thread.currentThread().getName() + "......" + e);
flag = false;
}
System.out.println(Thread.currentThread().getName() + "*******");
}
}
public void setFlag(){
this.flag = false;
}
}
class StopThreadDemo{
public static void main(String[] args){
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.setDaemon(true); // 開(kāi)啟t2守護(hù)線程(后臺(tái)線程)
t2.start();
int num = 1;
for(;;){
if(++num == 50){
t1.interrupt();
//t2.interrupt();
break;
}
System.out.println("main...." + num);
}
System.out.println("main....end");
}
}
線程的其他方法
- join()方法:函數(shù)執(zhí)行線程釋放執(zhí)行權(quán)和執(zhí)行資格,等待調(diào)用join()方法的線程執(zhí)行完成
- setPriority()方法:設(shè)置線程優(yōu)先級(jí)方法
- yield():暫停一下块攒,釋放一下CPU的執(zhí)行權(quán)励稳,同時(shí)自己還有機(jī)會(huì)搶到
class Demo implements Runnable{
public void run(){
for(int i = 0; i < 50; i++){
System.out.println(Thread.currentThread().toString() + "......" + i);
}
}
}
class JoinDemo{
public static void main(String[] args){
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t1.join(); // t1線程要申請(qǐng)加入進(jìn)來(lái),運(yùn)行囱井。臨時(shí)加入一個(gè)線程運(yùn)算時(shí)可以使用join()方法
t2.start();
t2.setPriority(Thread.MAX_PRIORITY);
for(int i = 0; i < 50; i++){
System.out.println(Thread.currentThread().getName() + "......" + i);
}
}
}
class ThreadTest{
public static void main(String[] args){
// main
for(int i = 0; i < 50; i++){
System.out.println(Thread.currentThread().getName() + "......" + i);
}
// Thread
new Thread(){
public void run(){
for(int j = 0; j < 50; j++){
System.out.println(Thread.currentThread().getName() + "......" + j);
}
}
}.start();
// Runnable
Runnable r = new Runnable(){
public void run(){
for(int k = 0; k < 50; k++){
System.out.println(Thread.currentThread().getName() + "......" + k);
}
}
}
new Thread(r).start();
}
}
Thread創(chuàng)建線程的時(shí)候可以進(jìn)行組的劃分驹尼,可以同時(shí)進(jìn)行一整個(gè)線程組的操作
面試案例
class Test implements Runnable {
public void run(Thread t){
}
}
// 如果錯(cuò)誤,錯(cuò)誤發(fā)生在哪一行庞呕?
/*
* 第一行:抽象類(lèi)runnable接口沒(méi)有被覆蓋新翎,要么被abstract修飾
* run是子類(lèi)的Test特有的方法
*/
class ThreadTest {
public static void main(String[] args){
// 如果都沒(méi)有,以Thread自己為主
new Thread(new Runnable(){
// 沒(méi)有子類(lèi)方法,以任務(wù)為主
public void run(){
System.out.println("Runnable run");
}
}){
// 子類(lèi)自己有方法料祠,以子類(lèi)為主
public void run(){
System.out.println("Thread run");
}
}.start();
}
}