国产TS紫迹丝袜高跟鞋在线,一区二区三区国产自产视频免费,67pao国产人成视频,午国产午夜激无码毛片不卡

愛碼網(wǎng)專注于資源免費(fèi)下載

Java多線程

一.基本概念

1.1 程序,進(jìn)程和線程

程序(program)是為了完成特定任務(wù),用某種語言編寫的一組指令的集合。即指一段靜態(tài)的代碼,靜態(tài)對(duì)象。
進(jìn)程(process)是程序的一次執(zhí)行過程,或是正在運(yùn)行的一個(gè)程序。是一個(gè)動(dòng)態(tài)的過程:有它自身的產(chǎn)生、存在和消亡的過程。——生命周期
如:運(yùn)行中的QQ,運(yùn)行中的MP3播放器
●程序是靜態(tài)的,進(jìn)程是動(dòng)態(tài)的
進(jìn)程作為資源分配的單位,系統(tǒng)在運(yùn)行時(shí)會(huì)為每個(gè)進(jìn)程分配不同的內(nèi)存
線程(thread),進(jìn)程可進(jìn)一步細(xì)化為線程,是一個(gè)程序內(nèi)部的一條執(zhí)行路徑。
●若一個(gè)進(jìn)程同一時(shí)間并行執(zhí)行多個(gè)線程,就是支持多線程的
線程作為調(diào)度和執(zhí)行的單位,每個(gè)線程擁有獨(dú)立的運(yùn)行棧和程序計(jì)數(shù)器(pc),線程切換的開銷小
●一個(gè)進(jìn)程中的多個(gè)線程共享相同的內(nèi)存單元/內(nèi)存地址空間?它們從同一堆中分配對(duì)象,可以訪問相同的變量和對(duì)象。這就使得線程間通信更簡(jiǎn)便、高效。但多個(gè)線程操作共享的系統(tǒng)資源可能就會(huì)帶來安全的隱患。

1.2 單核CPU和多核CPU

●單核CPU其實(shí)是一種假的多線程,因?yàn)樵谝粋€(gè)時(shí)間單元內(nèi),也只能執(zhí)行一個(gè)線程的任務(wù)。
●如果是多核的話,才能更好的發(fā)揮多線程的效率。(現(xiàn)在的服務(wù)器都是多核的)
●一個(gè)Java應(yīng)用程序java.exe,其實(shí)至少有三個(gè)線程:main()主線程,gc()垃圾回收線程,異常處理線程。當(dāng)然如果發(fā)生異常,會(huì)影響主線程

1.3 并行和并發(fā)

并行:多個(gè)CPU同時(shí)執(zhí)行多個(gè)任務(wù)。比如:多個(gè)人同時(shí)做不同的事。
并發(fā):一個(gè)CPU(采用時(shí)間片)“同時(shí)”執(zhí)行多個(gè)任務(wù)。比如:秒殺、多個(gè)人做同一件事。

1.4 多線程優(yōu)點(diǎn)

以單核CPU為例,只使用單個(gè)線程先后完成多個(gè)任務(wù)(調(diào)用多個(gè)方法),肯定比用多個(gè)線程來完成用的時(shí)間更短,為何仍需多線程呢?
多線程程序的優(yōu)點(diǎn):

  1. 提高應(yīng)用程序的響應(yīng)。對(duì)圖形化界面更有意義,可增強(qiáng)用戶體驗(yàn)。

  2. 提高計(jì)算機(jī)系統(tǒng)CPU的利用率

  3. 改善程序結(jié)構(gòu)。將既長(zhǎng)又復(fù)雜的進(jìn)程分為多個(gè)線程,獨(dú)立運(yùn)行,利于理解和修改

1.5 何時(shí)需要多線程

●程序需要同時(shí)執(zhí)行兩個(gè)或多個(gè)任務(wù)。
●程序需要實(shí)現(xiàn)一些需要等待的任務(wù)時(shí),如用戶輸入、文件讀寫操作、網(wǎng)絡(luò)操作、搜索等。

二.線程的創(chuàng)建和使用

2.1 繼承Thread類

●Java語言的JVM允許程序運(yùn)行多個(gè)線程,它通過java.lang.Thread類來體現(xiàn)。
Thread類的特性:
●每個(gè)線程都是通過某個(gè)特定Thread對(duì)象的run()方法來完成操作的,經(jīng)常把run()方法的主體稱為線程體
●通過該Thread對(duì)象的start()方法來啟動(dòng)這個(gè)線程,而非直接調(diào)用run

2.1.1 流程

(1)定義子類繼承Thread類。
(2)子類中重寫Thread類中的run方法。
(3)創(chuàng)建Thread子類對(duì)象,即創(chuàng)建了線程對(duì)象。
(4)調(diào)用線程對(duì)象start方法:?jiǎn)?dòng)線程,調(diào)用run方法。

2.1.2 注意點(diǎn)

(1)如果自己手動(dòng)調(diào)用run()方法,那么就只是普通方法,沒有啟動(dòng)多線程模式。
(2) run()方法由JVM調(diào)用,什么時(shí)候調(diào)用,執(zhí)行的過程控制都有操作系統(tǒng)的CPU調(diào)度決定。
(3)想要啟動(dòng)多線程,必須調(diào)用start方法。
(4)一個(gè)線程對(duì)象只能調(diào)用一次start()方法啟動(dòng),如果重復(fù)調(diào)用了,則將拋出以上的異?!癐llegalThreadStateException”。

2.1.3 代碼

package main.javabasic.thread;

/**
 * 創(chuàng)建多線程的方式一:繼承Thread類
 * 1.定義子類繼承Thread類。
 * 2.子類中重寫Thread類中的run方法。
 * 3.創(chuàng)建Thread子類對(duì)象,即創(chuàng)建了線程對(duì)象。
 * 4.調(diào)用線程對(duì)象start方法:?jiǎn)?dòng)線程,調(diào)用run方法:(1)啟動(dòng)當(dāng)前線程 (2)調(diào)用當(dāng)前線程的run()
 *
 * @author wangjian
 * @date 2020/4/20 22:24
 */
class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 1; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

public class ThreadTest1 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        // 問題一:我們不能通過直接調(diào)用run()的方式啟動(dòng)線程
        // myThread.run();

        // 問題二:再啟動(dòng)一個(gè)線程:不可以還讓已經(jīng)start()的線程去執(zhí)行
        // if (threadStatus != 0) throw new IllegalThreadStateException();
        // myThread.start();
        // 我們需要重新創(chuàng)建一個(gè)線程的對(duì)象
        MyThread myThread2 = new MyThread();
        myThread2.start();

        // 以下方法仍然是在main線程中執(zhí)行的
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i + "===========mian()=======");
            }
        }
    }
}

2.2 實(shí)現(xiàn)Runnable接口

2.2.1 流程

(1)定義子類,實(shí)現(xiàn)Runnable接口。
(2)子類中重寫Runnable接口中的run方法。
(3)通過Thread類含參構(gòu)造器創(chuàng)建線程對(duì)象。
(4)將Runnable接口的子類對(duì)象作為實(shí)際參數(shù)傳遞給Thread類的構(gòu)造器中。
(5)調(diào)用Thread類的start方法:開啟線程,調(diào)用Runnable子類接口的run方法。

2.2.2 代碼

package main.javabasic.thread;

/**
 * 創(chuàng)建多線程的方式二:實(shí)現(xiàn)Runnable接口
 * 1.創(chuàng)建一個(gè)實(shí)現(xiàn)了Runnable接口的類
 * 2.實(shí)現(xiàn)類去實(shí)現(xiàn)Runnable中的抽象方法:run()
 * 3.創(chuàng)建實(shí)現(xiàn)類的對(duì)象
 * 4.將此對(duì)象作為參數(shù)傳遞到Thread類的構(gòu)造器中,創(chuàng)建Thread類的對(duì)象
 * 5.通過Thread類的對(duì)象調(diào)用start()
 *
 * 比較創(chuàng)建線程的兩種方式:
 * 開發(fā)中優(yōu)先選擇實(shí)現(xiàn)Runnable接口的方式
 * 原因:1.實(shí)現(xiàn)的方式?jīng)]有類的單繼承性的局限性
 *       2.實(shí)現(xiàn)的方式更適合處理多個(gè)線程有共享數(shù)據(jù)的情況
 * 聯(lián)系:public class Thread implements Runnable
 * 相同點(diǎn):兩種方式都需要重寫run(),將線程要執(zhí)行的邏輯聲明在run()中
 *
 * @author wangjian
 * @date 2020/4/21 22:45
 */

class Mythread3 implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

public class ThreadTest2 {
    public static void main(String[] args) {
        Mythread3 mythread3 = new Mythread3();
        Thread t1 = new Thread(mythread3);
        t1.setName("線程1");
        // 通過Thread類的對(duì)象調(diào)用start():?jiǎn)?dòng)線程;調(diào)用當(dāng)前線程的run方法—>調(diào)用了Runnable類型的target的run()
        t1.start();
        Thread.currentThread().setName("主線程");
        for (int i = 0; i < 100; i++) {
            if (i % 2 != 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }

}

2.3 實(shí)現(xiàn)Callable接口

2.4 線程池

2.4.1 代碼

package main.javabasic.thread.create;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 創(chuàng)建多線程方式四:使用線程池
 * 好處:1.提高響應(yīng)速度(減少了創(chuàng)建新線程的時(shí)間)
 *       2.降低資源消耗(重復(fù)利用線程池中線程,不需要每次都創(chuàng)建)
 *       3.便于線程管理:
 *         corePoolSize:核心池的大小
 *         maximumPoolSize:最大線程數(shù)
 *         keepAliveTime:線程沒有任務(wù)時(shí)最多保持多長(zhǎng)時(shí)間后會(huì)終止
 *
 * @author wangjian
 * @date 2020/5/7 22:37
 */
class NumberThread implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

public class ThreadPoolTest {

    public static void main(String[] args) {
        // 1.提供指定線程數(shù)量的線程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        // 2.執(zhí)行指定的線程的操作。需要提供實(shí)現(xiàn)Runnable接口或Callable接口實(shí)現(xiàn)類的對(duì)象
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        //service1.setCorePoolSize(15);
        // System.out.println(service.getClass());
        service.execute(new NumberThread());  // 適合使用于Runnable
        // service.submit(Callable callable); // 適合使用于Callable
        // 關(guān)閉連接池
        service.shutdown();
    }
}

2.5 Thread類的有關(guān)方法

●void start(): 啟動(dòng)線程,并執(zhí)行對(duì)象的run()方法
●run(): 線程在被調(diào)度時(shí)執(zhí)行的操作
●String getName(): 返回線程的名稱
●void setName(String name):設(shè)置該線程名稱
●static Thread currentThread(): 返回當(dāng)前線程。在Thread子類中就是this,通常用于主線程和Runnable實(shí)現(xiàn)類
●static void yield():線程讓步
暫停當(dāng)前正在執(zhí)行的線程,把執(zhí)行機(jī)會(huì)讓給優(yōu)先級(jí)相同或更高的線程
若隊(duì)列中沒有同優(yōu)先級(jí)的線程,忽略此方法
●join() :當(dāng)某個(gè)程序執(zhí)行流中調(diào)用其他線程的 join() 方法時(shí),調(diào)用線程將被阻塞,直到 join() 方法加入的 join 線程執(zhí)行完為止
●低優(yōu)先級(jí)的線程也可以獲得執(zhí)行
●static void sleep(long millis):(指定時(shí)間:毫秒)
令當(dāng)前活動(dòng)線程在指定時(shí)間段內(nèi)放棄對(duì)CPU控制,使其他線程有機(jī)會(huì)被執(zhí)行,時(shí)間到后重排隊(duì)。
拋出InterruptedException異常
●stop(): 強(qiáng)制線程生命期結(jié)束,不推薦使用
●boolean isAlive():返回boolean,判斷線程是否還活著

三.線程的生命周期

Java多線程-第1張圖片

JDK中用Thread.State類定義了線程的幾種狀態(tài)
要想實(shí)現(xiàn)多線程,必須在主線程中創(chuàng)建新的線程對(duì)象。Java語言使用Thread類及其子類的對(duì)象來表示線程,在它的一個(gè)完整的生命周期中通常要經(jīng)歷如下的五種狀態(tài):
●新建: 當(dāng)一個(gè)Thread類或其子類的對(duì)象被聲明并創(chuàng)建時(shí),新生的線程對(duì)象處于新建狀態(tài)
●就緒:處于新建狀態(tài)的線程被start()后,將進(jìn)入線程隊(duì)列等待CPU時(shí)間片,此時(shí)它已具備了運(yùn)行的條件,只是沒分配到CPU資源
●運(yùn)行:當(dāng)就緒的線程被調(diào)度并獲得CPU資源時(shí),便進(jìn)入運(yùn)行狀態(tài), run()方法定義了線程的操作和功能
●阻塞:在某種特殊情況下,被人為掛起或執(zhí)行輸入輸出操作時(shí),讓出 CPU 并臨時(shí)中止自己的執(zhí)行,進(jìn)入阻塞狀態(tài)
●死亡:線程完成了它的全部工作或線程被提前強(qiáng)制性地中止或出現(xiàn)異常導(dǎo)致結(jié)束

四.線程的同步

4.1 同步問題

package main.javabasic.duoxiancheng;

/**
 * @author wangjian
 * @date 2020/6/28 21:42
 */
class Ticket implements Runnable {
private int ticket = 100;

@Override
public void run() {
    while (true) {
        if (ticket > 0) {
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "售出車票,ticket號(hào)為:" + ticket--);
        } else {
            break;
        }
    }
}
}

public class TicketDemo {

public static void main(String[] args) {
    Ticket t = new Ticket();
    Thread t1 = new Thread(t);
    Thread t2 = new Thread(t);
    Thread t3 = new Thread(t);
    t1.setName("t1窗口");
    t2.setName("t2窗口");
    t3.setName("t3窗口");
    t1.start();
    t2.start();
    t3.start();
}
}

1.多線程出現(xiàn)了安全問題
2.問題的原因:當(dāng)多條語句在操作同一個(gè)線程共享數(shù)據(jù)時(shí),一個(gè)線程對(duì)多條語句只執(zhí)行了一部分,還沒有執(zhí)行完,另一個(gè)線程參與進(jìn)來執(zhí)行。導(dǎo)致共享數(shù)據(jù)錯(cuò)誤。
3.解決方法:對(duì)多條操作共享數(shù)據(jù)的語句,只能讓一個(gè)線程都執(zhí)行完,在執(zhí)行的過程中,其他線程不可以參與執(zhí)行。

4.2 解決線程安全問題

Java對(duì)于多線程的安全問題提供了專業(yè)的解決方式:同步機(jī)制

4.2.1 Synchronized同步代碼塊

同步代碼塊
synchronized (對(duì)象){
// 需要被同步的代碼;
}

(1)同步代碼塊解決繼承Thread類的線程安全問題

package main.javabasic.thread.security;

/**
 * 在繼承Thread類創(chuàng)建多線程的方式中,慎用this充當(dāng)同步監(jiān)視器,考慮使用當(dāng)前類充當(dāng)同步監(jiān)視器
 *
 * @author wangjian
 * @date 2020/4/29 22:16
 */
class Window2 extends Thread {

    private static int ticket = 100;

    // static保證對(duì)象唯一
    private static Object obj = new Object();

    @Override
    public void run() {

        while (true) {
            // synchronized (obj) {
            synchronized (Window2.class) {
                // 類也是對(duì)象,Window2只會(huì)加載一次
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName() + ":賣票,票號(hào)為" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}

public class WindowTest2 {
    public static void main(String[] args) {
        Window2 t1 = new Window2();
        Window2 t2 = new Window2();
        Window2 t3 = new Window2();
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

(2)同步代碼塊解決實(shí)現(xiàn)Runable接口的線程安全問題

class Window implements Runnable {
    private int ticket = 100;

    Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            // synchronized (obj) {
            synchronized (this) {
                // 此時(shí)的this,指的是唯一的window1的對(duì)象
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":賣票" + ticket);
                    ticket--;
                }
            }

        }

    }
}

public class WindowTest1 {
    public static void main(String[] args) {
        Window window = new Window();
        Thread window1 = new Thread(window);
        Thread window2 = new Thread(window);
        Thread window3 = new Thread(window);
        window1.setName("窗口1");
        window2.setName("窗口2");
        window3.setName("窗口3");
        window1.start();
        window2.start();
        window3.start();
    }
}

4.2.2 Synchronized同步方法

同步方法
synchronized還可以放在方法聲明中,表示整個(gè)方法為同步方法。
例如:
public synchronized void show (String name){
….
}

(1)同步方法解決繼承Thread類的線程安全問題

class Window4 extends Thread {

    private static int ticket = 100;


    @Override
    public void run() {
        while (true) {
            show();
        }


    }

    private static synchronized void show() {
        // 同步監(jiān)視器:Window4.class
        if (ticket > 0) {
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":賣票,票號(hào)為" + ticket);
            ticket--;
        }
    }
}

public class WindowTest4 {
    public static void main(String[] args) {
        Window4 t1 = new Window4();
        Window4 t2 = new Window4();
        Window4 t3 = new Window4();
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

(2)同步方法解決實(shí)現(xiàn)Runable接口的線程安全問題

class Window3 implements Runnable {
    private int ticket = 100;

    @Override
    public void run() {
        while (true) {
            show();
        }
    }

    private synchronized void show() {
        // 同步監(jiān)視器this
        if (ticket > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":賣票" + ticket);
            ticket--;
        }
    }
}

public class WindowTest3 {
    public static void main(String[] args) {
        Window3 window = new Window3();
        Thread window1 = new Thread(window);
        Thread window2 = new Thread(window);
        Thread window3 = new Thread(window);
        window1.setName("窗口1");
        window2.setName("窗口2");
        window3.setName("窗口3");
        window1.start();
        window2.start();
        window3.start();
    }
}

4.2.3 Lock鎖

class Window5 implements Runnable {

    private int ticket = 100;

    // 1.實(shí)例化ReentrantLock
    ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                // 2.啟動(dòng)鎖
                lock.lock();
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ",售票號(hào):" + ticket);
                    ticket--;
                } else {
                    break;
                }
            } finally {
                // 3.結(jié)束鎖
                lock.unlock();
            }
        }
    }
}

public class LockTest {
    public static void main(String[] args) {
        Window5 window = new Window5();
        Thread t1 = new Thread(window);
        Thread t2 = new Thread(window);
        Thread t3 = new Thread(window);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

4.3 同步機(jī)制中的鎖

同步機(jī)制鎖
在《Thinking in Java》中,是這么說的:對(duì)于并發(fā)工作,你需要某種方式來防止兩個(gè)任務(wù)訪問相同的資源(其實(shí)就是共享資源競(jìng)爭(zhēng))。防止這種沖突的方法,就是當(dāng)資源被一個(gè)任務(wù)使用時(shí),在其上加鎖。第一個(gè)訪問某項(xiàng)資源的任務(wù)必須鎖定這項(xiàng)資源,使其他任務(wù)在其被解鎖之前,就無法訪問它了,而在其被解鎖之時(shí),另一個(gè)任務(wù)就可以鎖定并使用它了。
synchronized的鎖是什么?
●任意對(duì)象都可以作為同步鎖。所有對(duì)象都自動(dòng)含有單一的鎖(監(jiān)視器)。
●同步方法的鎖:靜態(tài)方法(類名.class)、非靜態(tài)方法(this)
●同步代碼塊:自己指定,很多時(shí)候也是指定為this或類名.class

注意:
●必須確保使用同一個(gè)資源的多個(gè)線程共用一把鎖,這個(gè)非常重要,否則就
無法保證共享資源的安全
●一個(gè)線程類中的所有靜態(tài)方法共用同一把鎖(類名.class),所有非靜態(tài)方
法共用同一把鎖(this),同步代碼塊(指定需謹(jǐn)慎)

4.4 同步的范圍

1、如何找問題,即代碼是否存在線程安全?(非常重要)
(1)明確哪些代碼是多線程運(yùn)行的代碼
(2)明確多個(gè)線程是否有共享數(shù)據(jù)
(3)明確多線程運(yùn)行代碼中是否有多條語句操作共享數(shù)據(jù)
2、如何解決呢?(非常重要)
對(duì)多條操作共享數(shù)據(jù)的語句,只能讓一個(gè)線程都執(zhí)行完,在執(zhí)行過程中,其
他線程不可以參與執(zhí)行。
即所有操作共享數(shù)據(jù)的這些語句都要放在同步范圍中
3、切記:
●范圍太?。簺]鎖住所有有安全問題的代碼
●范圍太大:沒發(fā)揮多線程的功能。

4.5 釋放鎖的操作

●當(dāng)前線程的同步方法、同步代碼塊執(zhí)行結(jié)束。
●當(dāng)前線程在同步代碼塊、同步方法中遇到break、return終止了該代碼塊、
該方法的繼續(xù)執(zhí)行。
●當(dāng)前線程在同步代碼塊、同步方法中出現(xiàn)了未處理的Error或Exception,導(dǎo)
致異常結(jié)束。
●當(dāng)前線程在同步代碼塊、同步方法中執(zhí)行了線程對(duì)象的wait()方法,當(dāng)前線
程暫停,并釋放鎖。

4.6 不會(huì)釋放鎖的操作

●線程執(zhí)行同步代碼塊或同步方法時(shí),程序調(diào)用Thread.sleep()、
Thread.yield()方法暫停當(dāng)前線程的執(zhí)行
●線程執(zhí)行同步代碼塊時(shí),其他線程調(diào)用了該線程的suspend()方法將該線程
掛起,該線程不會(huì)釋放鎖(同步監(jiān)視器)。
●應(yīng)盡量避免使用suspend()和resume()來控制線程


本文鏈接:http://fangxuan.com.cn/article/106.html

網(wǎng)友評(píng)論

熱門文章
隨機(jī)文章
熱門標(biāo)簽
側(cè)欄廣告位