java筆記--多線程系列二~Runnable

多線程的狀態(tài):

創(chuàng)建線程的第二種方式:實現(xiàn)Runnable接口

1 定義類實現(xiàn)Runnable接口
2 覆蓋接口中的run方法,將線程的任務(wù)代碼封裝到run方法中
3 通過Thread類創(chuàng)建線程對象,并將Runnable接口的子類對象作為Thread類的構(gòu)造函數(shù)的參數(shù)進(jìn)行傳遞
為什么?因為線程的任務(wù)都封裝在Runnable接口子類對象的run方法中。
所以要在線程對象創(chuàng)建時就必須明確要運(yùn)行的任務(wù)。
4 調(diào)用線程對象的strat方法開啟線程

Runnable的出現(xiàn)僅僅是將線程的任務(wù)進(jìn)行了對象的封裝。

實現(xiàn)Runnable接口的好處:

1 將線程的任務(wù)從線程的子類中分離出來,進(jìn)行單獨(dú)的封裝,
按照面向?qū)ο蟮乃枷雽⑷蝿?wù)封裝成對象。
2 避免了java單繼承的局限性

所以,創(chuàng)建線程的第二種方式比較常見。

class Demo  implements Runnable{//準(zhǔn)備擴(kuò)展Demo類的功能,讓其中的內(nèi)容可以
                                //作為線程的任務(wù)執(zhí)行,這時就需要通過接口的形式完成
    public void run(){
        show();
    }
    public void show(){
        for(int x=0;x<10;x++){
            System.out.println("....x="+x+"....name="+Thread.currentThread().getName());
        }
    }
}
public class RunnableDemo {

    public static void main(String[] args) {
        
        Demo d=new Demo();
        Thread t1=new Thread(d);
        Thread t2=new Thread(d);
        t1.start();
        t2.start();
    }
}

運(yùn)行:

多線程的安全問題:

線程可能會出現(xiàn)負(fù)數(shù)還能運(yùn)行的情況,即num=0,1,-1的情況,

class Ticket implements Runnable
{
    private int num=20;
    
    public void run(){
        while(true){
            if(num>0){
                try{//因為Runnable中run方法沒有Thows,所以這里只能用try-catch。
                    Thread.sleep(10);//理想狀態(tài)下不會出現(xiàn),但是不代表不會出現(xiàn),一旦出現(xiàn)就是致命問題,這里用sleep做停頓操作,使問題顯現(xiàn)出來
                }catch(InterruptedException e){
                    
                }
                System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
            }
        }
    }
}
public class LiZi {

    public static void main(String[] args) {
        
        Ticket t=new Ticket();//創(chuàng)建一個線程任務(wù)對象
//      Ticket tt=new Ticket();//一個對象表示一百張票
        
        Thread t1=new Thread(t);
        Thread t2=new Thread(t);
        Thread t3=new Thread(t);
        Thread t4=new Thread(t);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
}

運(yùn)行:

同步代碼塊:

問題出現(xiàn)就需要解決,如何解決呢?

解決思路:就是將多條操作共享數(shù)據(jù)的線程代碼封裝起來,當(dāng)有線程在執(zhí)行這些代碼的時候,其他線程是不可以參與運(yùn)算的。必須要當(dāng)前線程把這些代碼都執(zhí)行完畢后,其他線程才可以參與運(yùn)算。

而在java中用同步代碼塊就可以解決這個問題。
同步代碼塊的格式:
synchronized(對象){//這個對象就是鎖

      需要被同步的代碼塊;
}

解決:

class Ticket implements Runnable//extends Thread
{
    private int num=20;
    Object obj=new Object();//創(chuàng)建什么對象都可以
    
    public void run(){
        while(true){
            synchronized(obj)//同步代碼塊
            {
            if(num>0){
                try{//線程安全問題:線程可能會出現(xiàn)負(fù)數(shù)還能運(yùn)行的情況,即num=0,1,-1的情況,
                    Thread.sleep(10);//理想狀態(tài)下不會出現(xiàn),但是不代表不會出現(xiàn),一旦出現(xiàn)就是致命問題,這里用sleep做停頓操作,使問題顯現(xiàn)出來
                }catch(InterruptedException e){
                    
                }
                System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
                }
            }
        }
    }
}
public class LiZi {

    public static void main(String[] args) {
        
        Ticket t=new Ticket();//創(chuàng)建一個線程任務(wù)對象
//      Ticket tt=new Ticket();//一個對象表示一百張票
        
        Thread t1=new Thread(t);
        Thread t2=new Thread(t);
        Thread t3=new Thread(t);
        Thread t4=new Thread(t);
}
同步的好處:

解決了線程的安全問題(解決了一小部分而已)。

同步的弊端:

相對降低了效率,因為同步外的線程都會判斷同步鎖。

同步的前提:

必須有多個線程并使用同一個鎖。

不同鎖的情況:

Object obj=new Object();放進(jìn)run()方法中,就形成了四個鎖,一個線程一個鎖。

class Ticket implements Runnable//extends Thread
{
    private int num=20;
    //創(chuàng)建什么對象都可以
    
    public void run(){Object obj=new Object();
        while(true){
            synchronized(obj)//同步代碼塊   這里也可以直接用 Ticket.class  但最好還是obj的方式
            {
            if(num>0){
                try{//線程安全問題:線程可能會出現(xiàn)負(fù)數(shù)還能運(yùn)行的情況,即num=0,1,-1的情況,
                    Thread.sleep(10);//理想狀態(tài)下不會出現(xiàn),但是不代表不會出現(xiàn),一旦出現(xiàn)就是致命問題,這里用sleep做停頓操作,使問題顯現(xiàn)出來
                }catch(InterruptedException e){
                    
                }
                System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
                }
            }
        }
    }
}
public class LiZi {

    public static void main(String[] args) {
        
        Ticket t=new Ticket();//創(chuàng)建一個線程任務(wù)對象
        
        Thread t1=new Thread(t);
        Thread t2=new Thread(t);
        Thread t3=new Thread(t);
        Thread t4=new Thread(t);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
}

運(yùn)行:
例子:
/*
需求:儲戶,兩個,每個都到銀行存錢每次存一百,共存三次 。銀行總共收到多少錢
*/

class Bank{
    private int sum;//sum也是共享的
    private Object obj=new Object();
    public void add(int num){//找到安全隱患是在這發(fā)生的
        synchronized(obj){
        sum=sum+num;
        try{
            Thread.sleep(10);
        }catch(InterruptedException e){}
        System.out.println("sum="+sum);
        }
    }
}
class Cus implements Runnable{
    private Bank d =new Bank();//d是共享的
    public void run(){
//      Bank d =new Bank();不可以在這里,這這里就分成了兩個銀行各收三百
        for(int x=0;x<3;x++){
            d.add(100);
        }
    }
}
public class TongBu {

    public static void main(String[] args) {
        Cus c=new Cus();
        Thread t1=new Thread(c);
        Thread t2=new Thread(c);
        t1.start();
        t2.start();
    }
}

運(yùn)行:

將bank類改為:

class Bank{
    private int sum;//sum也是共享的
    //private Object obj=new Object();
    public synchronized void add(int num){//找到安全隱患是在這發(fā)生的
        //synchronized(obj){
        sum=sum+num;
        try{
            Thread.sleep(10);
        }catch(InterruptedException e){}
        System.out.println("sum="+sum);
        //}
    }
}

運(yùn)行:
這里的方法叫做同步函數(shù),效果等同于同步代碼塊。

同步函數(shù)使用的鎖是this;

同步函數(shù) 和同步代碼塊的區(qū)別:

同步函數(shù)的鎖是固定的this,是同步代碼塊的簡寫形式;同步代碼塊的鎖是任意的對象。

開發(fā)時建議使用同步代碼塊。
class Ticket implements Runnable//extends Thread
{
    private int num=100;
    boolean flag=true;
    
    public/* synchronized */void run(){
        if(flag){
            while(true){//這里不需要被同步,所以把if抽取到函數(shù)show里再調(diào)用
                synchronized(this){//所以這里用this,控制與同步奇函數(shù)的線程用同一個鎖
                if(num>0){
                    try{
                        Thread.sleep(10);
                    }catch(InterruptedException e){
                        
                    }
                    System.out.println(Thread.currentThread().getName()+"...daima..."+num--);
                    }   
            }
        }
            }
        else{
            while(true){
                this.show();//所以同步函數(shù)的鎖是this
            }
        }
    }
        public synchronized void show(){
            if(num>0){
                try{
                    Thread.sleep(10);
                }catch(InterruptedException e){
                    
                }
                System.out.println(Thread.currentThread().getName()+"...func..."+num--);
                }   
        }
    }
public class FuncLock {

    public static void main(String[] args) {
        Ticket t=new Ticket();

        
        Thread t1=new Thread(t);
        Thread t2=new Thread(t);
        t1.start();
        try{
            Thread.sleep(10);//先暫停主線程
            }catch(InterruptedException e){}
        t.flag=false;
        t2.start();
    }
}

運(yùn)行沒有數(shù)據(jù)重復(fù)或者出現(xiàn)0,-1的情況:

靜態(tài)的同步函數(shù)使用的鎖是 該函數(shù)所屬字節(jié)碼文件對象 可以用getclass()獲取,也可以用 類名.class 獲取 。

將Ticket類改為:
class Ticket implements Runnable//extends Thread
{
    private static int num=100;
    boolean flag=true;
    
    public/* synchronized */void run(){
        if(flag){
            while(true){//這里不需要被同步,所以把if抽取到函數(shù)show里再調(diào)用
                synchronized(this.getClass()){//因為是靜態(tài),所以要換成獲取字節(jié)碼文件,也可以用(Ticket.class)
                if(num>0){
                    try{
                        Thread.sleep(10);
                    }catch(InterruptedException e){
                        
                    }
                    System.out.println(Thread.currentThread().getName()+"...daima..."+num--);
                    }   
            }
        }
            }
        else{
            while(true){
                this.show();//所以同步函數(shù)的鎖是this
            }
        }
    }
        public static synchronized void show(){
            if(num>0){
                try{
                    Thread.sleep(10);
                }catch(InterruptedException e){
                    
                }
                System.out.println(Thread.currentThread().getName()+"...func..."+num--);
                }   
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容