1. 程式人生 > 實用技巧 >多執行緒學習筆記(狂神說視訊15-20)

多執行緒學習筆記(狂神說視訊15-20)

觀測執行緒狀態

public class TestState {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("......"+i+"......");
            } 
        });

        //觀察狀態
        Thread.State state = t.getState();
        System.out.println(state);

        //觀察啟動後
        t.start();//啟動執行緒
        state = t.getState();
        System.out.println(state);
        while (state!=Thread.State.TERMINATED){//執行緒不是終止狀態
            Thread.sleep(100);
            state = t.getState();//更新狀態
            System.out.println(state);
        }
        t.start();//停止之後就不能啟動了
    }
}

執行結果

執行緒的優先順序(priority)

執行緒優先順序高,不一定先呼叫

//測試執行緒的優先順序
public class TestPriority {

    public static void main(String[] args) {
        //主執行緒預設的優先順序
        System.out.println(Thread.currentThread().getName()+"->"+Thread.currentThread().getPriority());

        MyPriority m =new MyPriority();
        Thread t1 = new Thread(m);
        Thread t2 = new Thread(m);
        Thread t3 = new Thread(m);
        Thread t4 = new Thread(m);
        Thread t5 = new Thread(m);
        Thread t6 = new Thread(m);
        Thread t7 = new Thread(m);

        //先設定優先順序,再啟動
        t1.start();//不設定預設是5

        t2.setPriority(1);
        t2.start();

        t3.setPriority(4);
        t3.start();

        t4.setPriority(Thread.MAX_PRIORITY);//=10
        t4.start();

        t5.setPriority(7);
        t5.start();

        t6.setPriority(9);
        t6.start();
    }

}

class MyPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"->"+Thread.currentThread().getPriority());
    }
}

執行結果

守護執行緒(daemon)

//測試守護執行緒
//上帝守護你
public class TestDaemon {
    public static void main(String[] args) {
        You1 you1 = new You1();
        God god = new God();
        Thread thread = new Thread(god);
        thread.setDaemon(true);//預設false是使用者執行緒,正常的執行緒都是使用者執行緒
        thread.start();//上帝守護執行緒啟動
        new Thread(you1).start();//你 使用者執行緒啟動
    }
}
//你
class You1 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("你開心的活了"+i+"歲");
        }
        System.out.println("Goodbye World!");
    }
}
//上帝
class God implements Runnable{

    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("上帝守護著你");
        }
    }
}

執行結果

執行緒同步機制

併發:同一個物件被多個執行緒同時操作
執行緒同步:其實就是一種等待機制,多個需要同時訪問此物件的執行緒進入這個物件的等待池形成佇列,等待前面執行緒使用完畢,下一個執行緒再使用。

三大不安全案例

不安全的買票

public class UnsafeBuyTiket {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        BuyTicket station = new BuyTicket();
        new Thread(station,"苦逼的我").start();
        new Thread(station,"牛逼的大家").start();
        new Thread(station,"可惡的黃牛").start();
    }

}
class BuyTicket implements Runnable{
    //票
    private int ticketNums = 10;
    boolean flag = true;//外部停止方式
    @Override
    public void run() {
        //買票
        while(flag)
        {
            try{
                buy();
            }catch(InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
    private void buy() throws InterruptedException{
        //判斷是否有票
        if(ticketNums<=0)
        {
            return;
        }

        //模擬延時
        Thread.sleep(100);
        //買票
        System.out.println(Thread.currentThread().getName()+"拿到了"+ticketNums--+"票");
    }
}

執行結果

出現了重複的票

不安全的取錢

//兩個人去銀行取錢,賬戶
public class UnsafeBank {

    public static void main(String[] args) {

        Account a = new Account(100,"結婚基金");

        Drawing you = new Drawing(a,50,"你");
        Drawing girl = new Drawing(a,100,"girl");

        you.start();
        girl.start();

    }

}
//賬戶
class Account{
    int money;//餘額
    String name;//卡名
    public Account(int money,String name)
    {
        this.money = money;
        this.name = name;
    }
}
//銀行:模擬取款
class Drawing extends Thread{
    Account account;//賬戶
    int drawingMoney;//取了多少錢
    int nowMoney;//現在手裡有多少錢
    public Drawing(Account account,int drawingMoney,String name) {
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }
    //取錢
    @Override
    public void run() {
        //判斷有沒有錢
        if(account.money-drawingMoney<0)
        {
            System.out.println(Thread.currentThread().getName()+"錢不夠");
            return;
        }
        //sleep可以放大問題的發生性
        try {
            Thread.sleep(1000);
        }catch(Exception e)
        {
            e.printStackTrace();
        }
        //卡內餘額=餘額-你取的錢
        account.money = account.money-drawingMoney;
        //你手裡的錢
        nowMoney = nowMoney + drawingMoney;
        System.out.println(account.name+"餘額為"+account.money);

        //這兩個操作等價
        //System.out.println(this.getName()+Thread.currentThread().getName());
        System.out.println(this.getName()+"手裡的錢"+nowMoney);
    }
}

執行結果

出現了-50

不安全的集合

public class UnsafeList {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        List<String> list = new ArrayList<String>();
        for(int i=0;i<10000;i++)
        {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(3000);
        }catch(Exception e)
        {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }

}

執行結果

不足10000,因為存在兩個執行緒同時看到一個標誌,於是name就被覆蓋掉了

同步方法及同步塊(synchronized)

把不安全的改成安全的

同步的取票

//同步方法,鎖的是this
private synchronized  void buy() throws InterruptedException{
    //判斷是否有票
    if(ticketNums<=0)
    {
        return;
    }

    //模擬延時
    Thread.sleep(100);
    //買票
    System.out.println(Thread.currentThread().getName()+"拿到了"+ticketNums--+"票");
}

執行結果

同步的取款

//取錢
@Override
public void run() {
    synchronized(account)
    {
        //判斷有沒有錢
        if(account.money-drawingMoney<0)
        {
            System.out.println(Thread.currentThread().getName()+"錢不夠");
            return;
        }
        //sleep可以放大問題的發生性
        try {
            Thread.sleep(1000);
        }catch(Exception e)
        {
            e.printStackTrace();
        }
        //卡內餘額=餘額-你取的錢
        account.money = account.money-drawingMoney;
        //你手裡的錢
        nowMoney = nowMoney + drawingMoney;
        System.out.println(account.name+"餘額為"+account.money);

        //這兩個操作等價
        //System.out.println(this.getName()+Thread.currentThread().getName());
        System.out.println(this.getName()+"手裡的錢"+nowMoney);

    }
}

執行結果

同步的集合

for(int i=0;i<10000;i++)
{
    new Thread(()->{
        synchronized(list)
        {
            list.add(Thread.currentThread().getName());
        }
    }).start();
}

執行結果