線程

1.線程的實現(xiàn)

  • 實現(xiàn)線程的第一種方式,繼承thread類
class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName() + "--" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 實現(xiàn)線程的第二種方式,實現(xiàn)Runnable接口
class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName() + "--" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 調(diào)用線程
public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start(); //啟動線程

        //推薦
        MyRunnable mr = new MyRunnable();
        Thread t2 = new Thread(mr);
        t2.run();
    }

sleep()方法: 線程的休眠在當前線程的執(zhí)行中,暫停指定的毫秒數(shù),釋放CPU的時間片,具體取決于系統(tǒng)定時器和調(diào)度程序的精度和準確性,線程不會丟失任何監(jiān)視器的所有權(quán)


2.中斷線程

實例:
我們先來創(chuàng)建兩個線程

class MyRunnable2 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName() + "--" + i);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class MyRunnable3 implements Runnable{
    public boolean flag = true;
    public MyRunnable3(){
        flag = true;
    }
    @Override
    public void run() {
        int i = 0;
        while(flag){
            System.out.println(Thread.currentThread().getName()+"---"+(i++));
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

然后調(diào)用

public static void main(String[] args) {
        MyRunnable2 run1 = new MyRunnable2();
        Thread t = new Thread(run1);
        t.start();

        MyRunnable3 rm3 = new MyRunnable3();
        Thread t3 = new Thread(rm3);
        t3.start();

        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName()+"<->"+i);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (i==20){
                try {
                    t.join(); //讓t線程執(zhí)行完畢
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    //t3.interrupt();
                    rm3.flag = false;
                }
            }
        }
    }

表示main和t這兩個線程, 先是main和t同時執(zhí)行,等到main執(zhí)行到i=20時,main停下,等待t執(zhí)行完畢后,main再繼續(xù)執(zhí)行

中斷方法

join方法:
加入線程,讓調(diào)用的線程先執(zhí)行指定時間或執(zhí)行完畢
中斷線程:
(1) 使用interrupt()方法來中斷線程,設置一個中斷狀態(tài)(標記)sleep會自動清除中斷標記位,
(2) 自定義標記的方式 推薦


3 守護線程與yield

public static void main(String[] args) {
        MyRunnable4 rm4 = new MyRunnable4();
        Thread t4 = new Thread(rm4);
        t4.setName("主線程"); //設置線程名稱
        //設置線程優(yōu)先級,優(yōu)先級高可以提高線程搶占CPU時間片的概率
        t4.setPriority(Thread.MAX_PRIORITY);
        //線程可以分為守護線程和用戶線程,當進程中沒有用戶線程時,JVM會退出
        t4.setDaemon(true); //把線程設置為守護線程
        t4.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("main--"+i);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

線程的其他方法

  • public final void setDaemon(boolean on) on=true時表示設置為守護線程
  • public final boolean isDaemon()測試這個線程是否為守護線程
  • public static void yield()暫停當前正在執(zhí)行的線程對象,并執(zhí)行其他線程
  • long getId() 返回該線程的標識符
  • String getName() 返回該線程的名稱
  • void setName(String name) 設置該線程的名稱
  • boolean isAlive()他測試線程是否處于活動狀態(tài)
  • void setPriority(int newPriority) 更改線程的優(yōu)先級
  • static int MAX_PRIORITY 線程可以具有的最高優(yōu)先級
  • static int MIN_PRIORITY 線程可以具有的最低優(yōu)先級
  • static int NORM_PRIORITY 分配給線程的默認優(yōu)先級

4 線程同步

1. 多線程共享數(shù)據(jù)時,會發(fā)生線程不安全的情況
2. 多線程共享數(shù)據(jù)必須使用同步

線程同步的三種方法

1. 同步代碼塊

synchronized (this) {
            for (int i = 0; i < 300; i++) {
                if (ticket > 0) {
                    ticket--;
                    try {
                        Thread.sleep(1000);
                    } catch
                    (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(
                            "您購買的票已剩余:" +
                            ticket + "張");

                }
            }
        }

注意: synchronized()里面是一個同步鎖,只要是一個對象就可以了

2.同步方法

public synchronized void method() {
        for (int i = 0; i < 300; i++) {
            if (ticket > 0) {
                ticket--;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(
                        "您購買的票已剩余:" + ticket +
                                "張");

            }
        }
    }

注意: 實際上同步鎖是this

3. 使用Lock(更靈活的控制)

ReentrantLock lock = new ReentrantLock();//互斥鎖

    public void method2() {
        lock.lock();//鎖定
        try {
            for (int i = 0; i < 300; i++) {
                if (ticket > 0) {
                    ticket--;
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(
                            "您購買的票已剩余:" + ticket + "張");
                }
            }
        } finally {
            lock.unlock(); //釋放鎖
        }
  }

同步準則:

    1. 使代碼塊保持簡短,把不隨線程變化的預處理和后處理移出synchronized代碼塊
    1. 不要阻塞,如InputStream.read().
    1. 在持有鎖的時候,不要對其他對象調(diào)用同步方法

5 線程池

public static void main(String[] args) {
        //創(chuàng)建線程池
        //1. 創(chuàng)建一個單線程的線程池
        //ExecutorService es =Executors.newSingleThreadExecutor();

        //2. 創(chuàng)建一個固定大小的線程池
        //ExecutorService es =Executors.newFixedThreadPool(3);

        //3. 創(chuàng)建一個可緩存的線程池
        //ExecutorService es =Executors.newCachedThreadPool();

        //4. 創(chuàng)建一個大小無限的線程池
        ScheduledExecutorService sche = Executors.newScheduledThreadPool(3);
        ExecutorService es = sche;
        es.execute(new MyRunnable6());
        es.execute(new MyRunnable6());
        es.shutdown();
    }

6 生產(chǎn)者消費者案例

public class ThreadDemo6 {
    public static void main(String[] args) {
        Food food = new Food();
        Producter p = new Producter(food);
        Customers c = new Customers(food);
        Thread t1 = new Thread(p);
        Thread t2 = new Thread(c);
        t1.start();
        t2.start();
    }
}

/**
 * 消費者
 */
class Customers implements Runnable {
    private Food food;

    public Customers(Food food) {
        this.food = food;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            food.get();
        }
    }
}

/**
 * 生產(chǎn)者
 */
class Producter implements Runnable {
    private Food food;

    public Producter(Food food) {
        this.food = food;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i % 2 == 0) {
                food.set("鍋包肉", "酸甜口味");
            } else {
                food.set("佛跳墻", "美容養(yǎng)顏");
            }
        }
    }
}


class Food {
    private String name;
    private String desc;
    private boolean flag = true;  //true表示可以生產(chǎn),
    // false表示可以消費

    /**
     * 生產(chǎn)產(chǎn)品
     */
    public synchronized void set(String name,
                                 String desc) {
        //不能生產(chǎn)
        if (!flag) {
            try {
                this.wait(); //線程進入等待狀態(tài),
                // 釋放監(jiān)視器所有權(quán)(對象鎖)
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        if (flag) {
            this.setName(name);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.setDesc(desc);
            flag = false;
            this.notify(); //喚醒等待的線程(隨機的其中一個)
        }

    }

    /**
     * 消費產(chǎn)品
     */
    public synchronized void get() {
        //不能消費
        if (flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if (!flag) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(this.getName() + "--" + this.getDesc());
            flag = true;
            this.notifyAll();
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    @Override
    public String toString() {
        return "Food{" +
                "name='" + name + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }

    public Food(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }

    public Food() {
        super();
    }
}
  • 兩個線程協(xié)同工作: 先生產(chǎn),再消費
  • wait()里面如果加入時間,則能自動喚醒
  • 面試題: sleep和 wait的 區(qū)別?
  • sleep:讓線程進入休眠狀態(tài),讓出CPU的時間片,不釋放對象監(jiān)視器的所有權(quán)
  • wait: 讓線程進入等待狀態(tài),讓出CPU的時間片,并釋放對象監(jiān)視器的所有權(quán),
  • 等待其他線程通過notify()方法來喚醒


    java-thread.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • Java多線程學習 [-] 一擴展javalangThread類 二實現(xiàn)javalangRunnable接口 三T...
    影馳閱讀 3,110評論 1 18
  • 本文主要講了java中多線程的使用方法、線程同步、線程數(shù)據(jù)傳遞、線程狀態(tài)及相應的一些線程函數(shù)用法、概述等。 首先講...
    李欣陽閱讀 2,599評論 1 15
  • ??一個任務通常就是一個程序,每個運行中的程序就是一個進程。當一個程序運行時,內(nèi)部可能包含了多個順序執(zhí)行流,每個順...
    OmaiMoon閱讀 1,804評論 0 12
  • 一、線程的生命周期 線程狀態(tài)轉(zhuǎn)換圖: 1、新建狀態(tài) 用new關(guān)鍵字和Thread類或其子類建立一個線程對象后,該線...
    我是嘻哈大哥閱讀 1,020評論 0 8
  • 一、認識多任務、多進程、單線程、多線程 要認識多線程就要從操作系統(tǒng)的原理說起。 以前古老的DOS操作系統(tǒng)(V 6....
    GT921閱讀 1,094評論 0 3

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