多線程的深刻學習:單生產單消費,單生產多消費,多生產多消費,守護線程,線程優先級,join和yield,線程內部匿名類

 

如下三種狀況都是在須要生產出一個,立刻就消費一個的狀況下:安全

單生產單消費:多線程

單生產單消費是個簡單的多線程,只需保證多線程同步,而且鎖爲同一把鎖便可。代碼體現:函數

class Demo{this

private Object obj=new Object(); //建立鎖對象obj線程

private int count=0;對象

private boolean flag=false;       //建立標記接口

public void set(){get

synchronized(obj){             //同步線程同步

while(flag)產品

try {

obj.wait();             //線程等待

} catch (InterruptedException e) {

}

count++;

flag=true;

System.out.println("生產的產品"+count);

obj.notify();                //喚醒線程

}

}

public void sell(){

synchronized (obj) {

while(!flag)

try {

obj.wait();

} catch (InterruptedException e) {}

flag=false;

System.out.println("消費的產品"+count);

obj.notify();

}

}

}

class Set implements Runnable{

private Demo d;

public Set(Demo d){

this.d=d;

}

public void run(){

while(true){

d.set();

}

}

}

class Sell implements Runnable{

private Demo d;

public Sell(Demo d){

this.d=d;

}

public void run(){

while(true){

d.sell();

}

}

}

public class TextThread01 {

public static void main(String[] args) {

Demo d=new Demo();

Set st=new Set(d);

Sell sl=new Sell(d);

Thread t1=new Thread(st);

Thread t2=new Thread(sl);

t1.start();

t2.start();

}

}

以上是整個單生產單消費的程序,重點:1 保證鎖必須爲同一個,2線程等待喚醒的判斷邏輯沒問題。單生產多消費並不複雜.

多生產多消費和單生產和多消費就比單生產單消費複雜點了,按照上面的代碼的邏輯來處理多生產多消費的話,會出現多線程的安全問題。下面分析緣由:

先說單生產多消費的線程運行:生產線程1運行喚醒語句後可能還有執行權,而後繼續判斷,這時的flag爲true,生產線程1便進入等待。喚醒線程照樣執行,只是沒有意義。而後,這時有2個活的消費線程,其中一個啓動,運行結束後一樣可能有執行權,判斷(這時flag爲false),消費線程1就進入等待,交出鎖,由於這裏只有生產線程處於等待狀態,因此就喚醒線程1.到這裏,生產線程1和消費線程2都有執行資格,若是消費線程2拿到鎖,進入判斷,又進入等待,把鎖交出,只有生產線程1能夠拿到,生產線程1拿到後運行後進入等待,喚醒消費線程中的任意一個(這裏假設喚醒消費線程1),消費線程拿到執行權,執行完等待。這是有2個線程在等待,若是消費線程1喚醒的是消費線程2,這裏的falg是false,線程2就進入等待,這裏沒有喚醒任何線程,就出現了死鎖。因此單生產多消費用這樣的代碼實現是會出現安全問題的。

處理方案:將程序中的喚醒語句obj.notify換成obj.notifyAll能夠解決安全問題,推導參照上述推導便可。換成所有喚醒後,線程的運行的效率就會變得很低。要解決這裏的效率問題,須要用到jdk1.5新推出的locke類和其子類condition接口。lock類至關於鎖,condition至關於監視器。lock類相較於同步來講,優點在於能夠在一個鎖內建立2個監視器。

代碼體現:

class Demo{

private Object obj=new Object();

private int count=0;

private boolean flag=false;

private Lock lock=new ReentrantLock();

private Condition cot=lock.newCondition();

private Condition col=lock.newCondition();

public void set(){

//synchronized(obj){

lock.lock();try{

while(flag)

try {

cot.await();

} catch (InterruptedException e) {

}

count++;

flag=true;

System.out.println("生產的產品"+count);

col.signal();

//}

}finally{

lock.unlock();

}

}

public void sell(){

lock.lock();

try{

//synchronized (obj) {

while(!flag)

try {

col.await();

} catch (InterruptedException e) {}

flag=false;

System.out.println("消費的產品"+count);

cot.signal();

//}

}finally{

lock.unlock();

}

}

}

class Set implements Runnable{

private Demo d;

public Set(Demo d){

this.d=d;

}

public void run(){

while(true){

d.set();

}

}

}

class Sell implements Runnable{

private Demo d;

public Sell(Demo d){

this.d=d;

}

public void run(){

while(true){

d.sell();

}

}

}

public class TextThread01 {

public static void main(String[] args) {

Demo d=new Demo();

Set st=new Set(d);

Sell sl=new Sell(d);

Thread t1=new Thread(st);

Thread t2=new Thread(sl);

Thread t3=new Thread(sl);

t1.start();

t2.start();

t3.start();

}

}

多消費多生產和單生產單消費的機制是同樣的,只需再建立一個生產對象便可,代碼體現如上。

守護線程:

將線程定位守護線程後,該線程的特性爲:當該多線程中只剩下守護線程在運行時,守護線程就中止運行。代碼體現:

class Daemon extends Thread{

private int count=0;

public void print(){

System.out.println(Thread.currentThread().getName()+"***********"+count);

count++;

}

public void run(){

for(int i=0;i<1000;i++){

print();

}

}

}

public class TextDaemon01 {

public static void main(String[] args) {

//TextDaemon01 td=new TextDaemon01();

Daemon d=new Daemon();

Daemon d1=new Daemon();

d1.start();

d.setDaemon(true);

//d.setDaemon(true);

d.start();

}

}

這裏有個疑問:主函數能不能做爲守護線程?

線程優先級:

getPriority是返回線程的優先級,setPriority爲設置線程的優先級。線程優先級爲0-10級,咱們設置的時候爲了能夠獲得明顯的線程優先效果,通常設置爲MAX_PRIORITY,MINPRIORITY,NORM_PRIORITY.

Join yield方法:

Join方法的意思:線程2是加入一個線程1,線程1將釋放執行全,等線程2執行完畢後,線程1纔開始繼續運行。先看代碼體現:

public class TextDaemon01 {

public static void main(String[] args) throws InterruptedException {

//TextDaemon01 td=new TextDaemon01();

Daemon d=new Daemon();

Daemon d1=new Daemon();

d.start(); //語句1

d.join();//語句2

d1.start();//語句3

for(int i=0;i<100;i++){

System.out.println(Thread.currentThread().getName()+i);

}

}

}

語句123如今這個狀況就是主函數線程運行到語句1時,線程1啓動,語句2時主函數線程開始等待線程1的執行完畢,再和線程2搶奪執行權開始運行。

d.start(); //語句1

d1.start();//語句3

d.join();//語句2

當語句123是上面的狀況時,線程12交替運行,等線程1運行完畢後主函數線程纔開始運行。這裏說明一點:主線程釋放執行權不是指向的,活線程均可以搶奪執行權並執行。

Yield:

這個方法的意思:讓線程暫時暫停,可是該線程可能獲取到執行權。

線程在匿名內部類的的表現:

代碼體現:

public class TextThread02 {

public static void main(String[] args) {

new Thread(){

public void run(){

while(true){

System.out.println("......");

}

}

}.start();;

}

}

就是將線程寫入匿名內部類,功能沒變化。

相關文章
相關標籤/搜索