Java中線程的狀態、同步鎖以及死鎖詳解

線程的狀態

線程的六種狀態
    1.新建狀態(new 線程對象)
    2.運行狀態(調用start方法)
    3.受阻塞狀態(等待CPU的執行資源)
    4.休眠狀態(調用了sleep(時間)方法)
    5.等待狀態(調用了wait方法)
    6.死亡狀態(run執行完畢)

線程的狀態

匿名內部類

線程的第三種創建方式:
    匿名內部類方式:相當於創建了一個該類的子類對象
    new 父類或接口名(){
        重寫父類方法
    };
    這裏new出來的就是這個類的子類對象

示例代碼:    
    class Test{
        public void fun() {
            System.out.println("我是父類的fun方法");
        }
    }
    public static void fun1() {
        // 創建Test類的子類對象
        Test test =  new Test() {
            // 重寫父類方法
            @Override
            public void fun() {
                System.out.println("我是子類的fun方法");
            }
        };
        // 調用子類方法
        test.fun();
    }

接口:
    interface TestInter{
        public abstract void fun();
    }
    public static void fun2() {
        // 創建接口的實現類
        new TestInter() {

            @Override
            public void fun() {
                System.out.println("我是實現類的fun方法");
            }
        }.fun();
    }

需求:利用匿名內部類方式給TreeSet集合中的學生對象按年齡排序
        TreeSet<Student> set = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getAge() - o2.getAge();
            }
        });
        set.add(new Student("sc", 18));
        set.add(new Student("kd", 19));
        set.add(new Student("kb", 17));
        for (Student student : set) {
            System.out.println(student);
        }

匿名內部類創建線程

繼承:
        new Thread() {
            @Override
            public void run() {
                System.out.println("-----");
            }
        }.start();

接口:
        Runnable runnable = new Runnable() {

            @Override
            public void run() {
                System.out.println("|||||");
            }
        };
        new Thread(runnable).start();

合一起:
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("`````");
            }
        });
        thread.start();

線程休眠

sleep()的作用是讓當前線程休眠,即當前線程會從運行狀態進入到休眠狀態。
sleep()會指定休眠時間

代碼示例:
    public class Demo04 {
        public static void main(String[] args) throws InterruptedException {
            for (int i = 0; i < 100; i++) {
                // 線程休眠一秒 單位毫秒
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + i);
            }
        }
    }

    class SleepThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i < 50; i++) {
                /*
                 * 如果在子線程中出現異常只能try ... catch處理
                 * Thread類是Runnable接口的實現類
                 * 重寫接口中的run方法
                 * 由於該方法沒有拋出異常
                 * 所以所有Runnable接口的實現類(包括Thread類) 
                 * 都不能在run方法中拋出異常只能處理
                 */
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + i);
            }
        }
    }

同步鎖(同步代碼塊)

同步鎖:鎖可以是任意對象,要保證鎖的唯一

同步鎖規則:
    線程遇到鎖可以進入同步代碼塊(並且攜帶鎖)
    當線程執行代碼塊中的代碼後 把鎖返還
    線程沒有遇到鎖 會在同步代碼塊外等待 遇到鎖才能進

賣火車票問題:
    public class Demo {
        public static void main(String[] args) {
            Tickets t = new Tickets();
            // 創建3個線程 這3個線程會執行run方法(這條線程的任務)
            Thread t1 = new Thread(t);
            Thread t2 = new Thread(t);
            Thread t3 = new Thread(t);
            // 開啓3個線程
            t1.start();
            t2.start();
            t3.start();
        }
    }

class  Tickets implements Runnable{
    // 聲明50張票 保證票是共享數據 只new 一次該類對象
    private int tickets = 50;
    // 創建了對象鎖 保證唯一
    private Object obj = new Object();
    // 賣票方法
    @Override
    public void run() {
        while (true) {
            // 鎖只要保證是對象和唯一就可以 填this也可以
            synchronized (obj) {
                // 操作的共享數據的代碼
                if (tickets > 0) {
                    // 讓線程休眠
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // 有票就賣
                    System.out.println(Thread.currentThread().getName() + "--" + tickets);
                    // 減少一張
                    tickets--;
                } else {
                    // 沒票 結束循環
                    break;
                }
            }
            // 讓線程讓出CPU的資源
            Thread.yield();
        }
    }   
}

在方法中添加synchronized關鍵詞 把方法變成 同步方法
class  Tickets1 implements Runnable{

    private int tickets = 50;
    // 創建了對象鎖 保證唯一
    private Object obj = new Object();
    // 賣票方法
    @Override
    public void run() {
        while (true) {
            if (sellTickets()) {
                break;
            }
            // 讓線程讓出CPU的資源
            Thread.yield();
        }
    }
    // 操作共享數據的方法
    public synchronized boolean sellTickets() {

        // 靜態方法的同步代碼的鎖可以使用本類 類名.class
        // public static boolean sellTickets() {
        //  synchronized (Object.class) {}

        // 操作的共享數據的代碼
        if (tickets > 0) {
            // 讓線程休眠
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 有票就賣
            System.out.println(Thread.currentThread().getName() + "--" + tickets);
            // 減少一張
            tickets--;
            return false;
        } else {
            // 沒票 結束循環
            return true;
        }
    }
}

死鎖

死鎖:在編寫多線程的時候,必須要注意資源的使用問題,如果兩個或多個線程分別擁有不同的資源, 
而同時又需要對方釋放資源才能繼續運行時,就會發生死鎖。比如如果線程1持有鎖A並且想獲得鎖B,線程2持有鎖B並且想獲得鎖A,那麼這兩個線程將永遠等待下去,產生了死鎖

模擬線程死鎖:
    public class Demo07 {
        public static void main(String[] args) {
            DieLock dl = new DieLock();
            Thread t1 = new Thread(dl);
            Thread t2 = new Thread(dl);
            t1.start();
            t2.start();
        }
    }

    class LockA {
        // 私有構造方法
        private LockA() {
        }
        // 定義一個常量作爲鎖對象 不能修改(也不能創建)
        public static final LockA LOCK_A = new LockA();
    }

    class LockB {
        // 私有構造方法
        private LockB() {
        }
        // 定義一個常量作爲鎖對象 不能修改(也不能創建)
        public static final LockB LOCK_B = new LockB();
    }

    // 死鎖
    class DieLock implements Runnable{  
        // 聲明一個標記 一次是先A後B 一次是先B後A
        private boolean isTrue = true;
        @Override
        public void run() {
            // 死循環 增加死鎖機率
            while (true) {
                // 按標記
                if (isTrue) {
                    // A鎖到B鎖
                    synchronized (LockA.LOCK_A) {
                        System.out.println("LOCK_A");
                        synchronized (LockB.LOCK_B) {
                            System.out.println("LOCK_B");
                        }
                    }
                } else {
                    // B鎖到A鎖
                    synchronized (LockB.LOCK_B) {
                        System.out.println("LOCK_B");
                        synchronized (LockA.LOCK_A) {
                            System.out.println("LOCK_A");
                        }
                    }
                }
                // 修改標記
                isTrue = !isTrue;
            }
        }   
    }

Lock接口

JDK1.5 鎖 Lock接口
使用Lock鎖
    lock.lock();
    try{
        寫操作共享數據的代碼
    } finally {
        lock.unlock();
    }

接口實現創建線程好處:
    1.避免直接繼承Thread類的侷限性(避免單繼承)
    2.接口即插即用 減少類與類之間的聯繫(可以解耦)

使用接口Lock接口鎖:
    public class Demo {
        public static void main(String[] args) {
            Tickets3 tickets3 = new Tickets3();
            // 創建3條線程 這條線程會執行 run方法(這條線程的任務)
            Thread t1 = new Thread(tickets3);
            Thread t2 = new Thread(tickets3);
            Thread t3 = new Thread(tickets3);
            // 開啓3個線程
            t1.start();
            t2.start();
            t3.start();
        }
    }

    class Tickets3 implements Runnable{

        private int tickets = 50;
        // 聲明鎖對象
        private ReentrantLock lock = new ReentrantLock();

        @Override
        public void run() {
            while (true) {
                // 加鎖
                lock.lock();
                try {
                    // 鎖住操作共享數據的代碼
                    if (tickets > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "--" + tickets);
                        tickets--;
                    } else {
                        break;
                    }
                } finally {
                    // 解鎖
                    lock.unlock();
                }
                Thread.yield();
            }
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章