java——多執行緒

凌晨裡的無聊人發表於2020-11-01

1.什麼是程式

狹義:程式是正在執行的程式例項

廣義:程式是一個具有獨立功能的程式關於某個資料集合的一次執行活動,是系統進行資源分配和排程的基本單位

程式的概念要注意兩點:

程式是一個實體。每一個程式都有它自己的獨立地址空間
程式是一個“執行中的程式”。只有當處理器執行這個程式時,它成為活動的實體
2.什麼是執行緒

   執行緒是程式中的一個實體,是CPU排程的基本單位,只擁有在執行中必不可少的資源,但它可與同屬一個程式的其他執行緒共享所擁有的資源。

3.執行緒和程式的關係

執行緒是程式的一個實體,一個程式可以擁有多個執行緒。

4.程式和執行緒的區別

(1) 地址空間:同一程式的所有執行緒共享本程式的地址空間,不同程式之間的地址空間是相互獨立的。
(2) 資源擁有:同一程式內的執行緒共享本程式的資源如:記憶體、I/O,CPU等,不同的程式資源相互獨立。
(3) 健壯性:一個程式崩潰後,不會對其他程式產生影響,但是一個執行緒崩潰後,會終結整個程式。所以多程式要比多執行緒健壯。
(4) 執行緒切換比程式切換消耗的資源更少、速度更快。
(5) 執行緒是CPU排程的基本單位,程式是系統資源分配的基本單位。
排程問題
在這裡插入圖片描述
同步與非同步
在這裡插入圖片描述
併發與並行
併發指兩個或多個事件在同一個時間段內發生。
並行指兩個或多個事件在同一時刻發生(同時發生)
開啟兩個執行緒,使用搶佔式的執行方式
在這裡插入圖片描述
在這裡插入圖片描述
他們的執行方式是這樣的
在這裡插入圖片描述
在這裡插入圖片描述
Runnable介面
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
設定和獲取執行緒名稱
在這裡插入圖片描述

public class setname {
    public static void main(String[] args) {
        //如何獲取執行緒的名稱
        System.out.println(Thread.currentThread().getName());
        //兩種設定執行緒名稱的方式
        Thread t = new Thread(new MyRunnable());
        t.setName("wwww");
        t.start();
        new Thread(new MyRunnable(),"鋤禾日當午").start();
        //不設定的有預設的名字
        new Thread(new MyRunnable()).start();
    }
    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }
}

下邊的執行緒不是固定的,執行緒是搶佔執行的
順序不是固定的是


import  java.lang.Thread;
public class THread {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
        //兩種方式去建立執行緒
        MyRunnable mr = new MyRunnable();
        Thread t = new Thread(mr);
        t.start();
        new Thread(new MyRunnable()).start();
    }
}
 class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

在這裡插入圖片描述
指定時間休眠Sleep

public class setname {
    public static void main(String[] args) {
      new Thread(new MyRunnable()).start();
    }
    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("已經休眠完成");
        }
    }
}

在這裡插入圖片描述
執行緒阻塞就是所有消耗時間的操作。如果用stop直接結束執行緒,那麼它沒辦法及時的把它所擁有的資源去釋放
執行緒的中斷(中斷只是一個標記,告訴執行緒該死亡了,然後線上程中新增死亡的方法)

import sun.management.ThreadInfoCompositeData;

public class setname {
    public static void main(String[] args) {
     MyRunnable r1=new MyRunnable();
     Thread t1=new Thread(r1);
      t1.start();
      t1.interrupt();
    }
    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                //e.printStackTrace();
                System.out.println("發現了中斷標記該結束了");
                return;
            }
            System.out.println("已經休眠完成");
        }
    }
}

在這裡插入圖片描述
在實際程式設計中一般不用stop,stop會強制關閉,如果當時該執行緒沒有釋放所擁有的資源會造成麻煩

守護執行緒
使用者執行緒:當一個程式不包含任何的存活使用者執行緒時,進行結束
守護執行緒: 守護使用者執行緒的,當最後一個使用者執行緒結束時,所有守護執行緒自 動死亡

import sun.management.ThreadInfoCompositeData;

public class setname {
    public static void main(String[] args) throws InterruptedException {
     MyRunnable r1=new MyRunnable();
     Thread t1=new Thread(r1);
     //設定t1為守護執行緒,在本例中只有main一個使用者執行緒
      t1.setDaemon(true);
      t1.start();
     for(int i=1;i<5;i++){
         System.out.println(Thread.currentThread().getName()+":"+i);
         Thread.sleep(1000);
     }
    }
    static class MyRunnable implements Runnable{

        @Override
        public void run() {
            for(int i=0;i<10;i++){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    //e.printStackTrace();
                    System.out.println("發現了中斷標記該結束了");
                    return;
                }

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

        }
    }
}

在本例中,守護執行緒應該執行10次,但是當使用者執行緒main執行完成後,就結束了在這裡插入圖片描述
執行緒的安全與不安全

public class setname {
    public static void main(String[] args) {
        //執行緒不安全
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //總票數
        private int count = 10;
        @Override
        public void run() {
            while (count>0){
                //賣票
                System.out.println("正在準備賣票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println("賣票結束,餘票:"+count);
            }
        }
    }
}

在這裡插入圖片描述
當餘票剩餘1 的情況下,A進入catch但是此時丟失了時間片,被B拿到,此時count的值還沒來得及改變於是會發生-1張票的情況
在這裡插入圖片描述
執行緒安全的解決方案一——同步程式碼塊(上鎖 ——synchronized,但是上完鎖以後效率就變得不高了,誰先搶到,誰更容易搶到接下來的)
三個執行緒看同一把鎖 Object O

//執行緒同步synchronized

public class setname {
    public static void main(String[] args) {
        Object o = new Object();
        //執行緒不安全
        //解決方案1  同步程式碼塊
        //格式:synchronized(鎖物件){
        //
        //
        //      }
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //總票數
        private int count = 10;
        private Object o = new Object();
        @Override
        public void run() {
            //Object o = new Object();    //這裡不是同一把鎖,所以鎖不住
            while (true) {
                synchronized (o) {
                    if (count > 0) {
                        //賣票
                        System.out.println("正在準備賣票");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName()+"賣票結束,餘票:" + count);
                    }else {
                        break;
                    }

                }
            }
        }
    }
}

可以看到,方法一一旦哪個執行緒搶到了,那麼接下來就會執行很方便
在這裡插入圖片描述
反例,如果Object O放在run裡邊,那麼三個執行緒在建立的時候都看各自的鎖,此時就是執行緒不安全的
在這裡插入圖片描述
方法二 同步方法

//執行緒同步synchronized

public class setname {
    public static void main(String[] args) {
        Object o = new Object();
        //執行緒不安全
        //解決方案2  同步方法
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }
    static class Ticket implements Runnable{
        //總票數
        private int count = 10;
        @Override
        public void run() {
            while (true) {
                boolean flag = sale();
                if(!flag){
                    break;
                }
            }
        }
        public synchronized boolean sale(){
            if (count > 0) {
                //賣票
                System.out.println("正在準備賣票");
                try {
                    Thread.sleep(1500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName()+"賣票結束,餘票:" + count);
                return true;
            }
            return false;

        }
    }
}

需要注意的是這裡
在這裡插入圖片描述
如果裡邊new 三個Ticket
排隊的物件是裡邊建立的這個Ticket,現在裡邊新建立的,三個,會呼叫三個run,沒有實現排隊機制,而之前裡邊放的是run,三個執行緒使用他會被鎖,實現排隊機制,但是上面這種方式不行

方法三——顯示鎖
一定注意鎖的引數,如果是false,那麼很大可能被某一個執行緒獨佔資源,其他執行緒得不到資源的分配

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//同步程式碼塊和同步方法都屬於隱式鎖
//執行緒同步lock

public class setname {
    public static void main(String[] args) {
        //執行緒不安全
        //解決方案1   顯示鎖  Lock  子類 ReentrantLock
        Runnable run = new Ticket();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket implements Runnable{
        //總票數
        private int count = 10;
        //引數為true表示公平鎖    預設是false 不是公平鎖
        private Lock l = new ReentrantLock(true);
        @Override
        public void run() {
            while (true) {
                l.lock();
                if (count > 0) {
                    //賣票
                    System.out.println("正在準備賣票");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count--;
                    System.out.println(Thread.currentThread().getName()+"賣票結束,餘票:" + count);
                }else {
                    break;
                }
                l.unlock();
            }
        }
    }
}

顯示鎖和隱式鎖的區別:
https://blog.csdn.net/ZL_do_it/article/details/106059939?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160422284619195264700274%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=160422284619195264700274&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v28-1-106059939.first_rank_ecpm_v3_pc_rank_v2&utm_term=%E6%98%BE%E7%A4%BA%E9%94%81%E5%92%8C%E9%9A%90%E5%BC%8F%E9%94%81%E7%9A%84%E5%8C%BA%E5%88%AB&spm=1018.2118.3001.4449

公平鎖:誰先到誰得到鎖
非公平鎖:大家一塊搶
在這裡插入圖片描述
在這裡插入圖片描述
上邊是喚醒所有執行緒,下邊是讓當前執行緒睡著。
多執行緒通訊——生產者消費者問題,就像是廚師和服務員一樣,當廚師在工作的時候,服務員執行緒在wait(沉睡),當服務員送餐的時候廚師在(wait)

public class setname {
    public static void main(String[] args) {
        //多執行緒通訊    生產者與消費者問題
        Food f = new Food();
        new Cook(f).start();
        new Waiter(f).start();
    }
    //廚師
    static class Cook extends Thread{
        private Food f;

        public Cook(Food f) {
            this.f = f;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                if(i%2==0){
                    f.setNameAndTaste("老乾媽小米粥","香辣味");
                }else {
                    f.setNameAndTaste("煎餅果子","甜辣味");
                }
            }
        }
    }
    //服務員
    static class Waiter extends Thread{
        private Food f;

        public Waiter(Food f) {
            this.f = f;
        }
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                f.get();
            }
        }
    }
    //食物
    static class Food{
        private String name;
        private String taste;
        //true表示可以生產
        boolean flag = true;
        public synchronized void setNameAndTaste(String name,String taste){
            if(flag){
                this.name = name;
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.taste = taste;
                flag = false;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
        public synchronized void get(){
            if(!flag){
                System.out.println("服務員端走的菜的名稱是:"+name+",味道是:"+taste);
                flag = true;
                this.notifyAll();
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

執行緒的狀態
在這裡插入圖片描述
執行緒的狀態轉換
在這裡插入圖片描述
Java中第三種執行緒Callable實現方式(作為了解)
在這裡插入圖片描述
在這裡插入圖片描述
返回值會先列印下邊的123456789,然後get方法等待子執行緒執行完成後再執行他自己的執行緒在這裡插入圖片描述
判斷執行緒是否執行完在這裡插入圖片描述
取消執行緒在這裡插入圖片描述
執行緒池是容納多個執行緒的容器(相當於一個執行緒陣列)開發中很少使用執行緒池。(java中有四種執行緒池)
在這裡插入圖片描述
快取執行緒池
向執行緒池中加入任務(指揮執行緒池執行新的任務)在這裡插入圖片描述
在這裡插入圖片描述
再複製那個方法,因為執行緒池中已經有了快取,所以會呼叫在快取中的
在這裡插入圖片描述
定長執行緒池在這裡插入圖片描述
單執行緒執行緒池在這裡插入圖片描述
執行完不會關閉還在等待傳入,但是在一定時間後還會自動關閉的
週期執行緒池在這裡插入圖片描述
Lambda表示式
以前建立執行緒的方式在這裡插入圖片描述
匿名內部類的使用(比原來簡便一些)
在這裡插入圖片描述
函數語言程式設計
在這裡插入圖片描述
介面只有一個方法才可以使用lambda表示式在這裡插入圖片描述
修改後的在這裡插入圖片描述

相關文章