java核心技術-多線程基礎

java核心技術-多線程基礎
進程、線程java

​ 進程(Process) 是程序的運行實例。例如,一個運行的 Eclipse 就是一個進程。進程是程序向操做系統申請資源(如內存空間和文件句柄)的基本單位。線程(Thread)是進程中可獨立執行的最小單位。一個進程能夠包含多個線程。進程和線程的關係,比如一個營業中的飯店與其正在工做的員工之間的關係。編程

1.1 線程的建立、啓動與運行安全

在 Java 中實現多線程主要用兩種手段,一種是繼承 Thread 類,另外一種就是實現 Runnable 接口。(固然還有Callable和線程池)。下面咱們就分別來介紹這兩種方式的使用,其餘請關注此博客下文。多線程

(1).繼承Thread的類app

public class PrimeThread extends Thread{br/>//線程執行體
@Override
public void run(){ide

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

}

}
public class TestThread {
public static void main(String[] args) {
//新建一個線程
PrimeThread p1 = new PrimeThread();
//啓動一個線程
p1.start();this

PrimeThread p2 = new PrimeThread();
    p2.start();

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

}

}
(2).實現 Runnable接口操作系統

public class Ticket implements Runnable{線程

private int ticket = 100;

@Override
public void run() {
    while(ticket > 0){
        System.out.println(Thread.currentThread().getName() + "=" + --ticket);
    }

}

}
public class TestThread2 {
public static void main(String[] args) {code

Ticket ticket = new Ticket();

    //雖然是實現了Runnable接口 本質上只是實現了線程執行體 啓動工做仍是須要Thread類來進行
    Thread t1 = new Thread(ticket,"售票窗口一");
    t1.start();

    Thread t2 = new Thread(ticket,"售票窗口二");
    t2.start();

    Thread t3 = new Thread(ticket,"售票窗口三");
    t3.start();
}

}
兩種實現方式的對比:

1.從面向對象編程角度看:第一種建立方式(繼承Thread類) 是一種基礎繼承的技術,第二種建立方式(以Runnable接口實例爲構造器參數直接經過new建立Thread實例)是一種基礎組合的技術。方式二不只會避免單繼承的尷尬,也會下降類與類之間的耦合性。

2.從對象共享角度看:第二種建立方式意味着多個線程實例能夠共享同一個Runnable實例。而第一種方式則須要依賴static關鍵字來完成操做。

1.2 線程的控制

Java的調度方法

同優先級線程組成先進先出隊列(先到先服務),使用時間片策略

對高優先級,使用優先調度的搶佔式策略

Thread類的相關方法:

sleep(long millis) : 是 Thread 類中的靜態方法,使當前線程進入睡眠狀態
join() / join(long millis) : 是一個實例方法,使當前線程進入阻塞狀態
interrupt() : 中斷阻塞狀態的線程
isAlive() : 判斷當前線程是否處於存活狀態
yield() : 線程讓步
public class TestThread3 {
public static void main(String[] args) throws Exception {

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

        }
    },"線程1");
    t1.start();
    //線程1在 sleep以前就執行完了
    t1.sleep(10000);
    //join方法 迫使t2 必須等線程1 執行完 才能執行 然而 t1輸出完本身的 睡着了 t2被迫等了10秒
    t1.join();

    Thread t2 = new Thread(new Runnable() {
        @Override
        public void run() {
            for(int i = 1; i < 100;i++){
                if(i % 2 != 0){
                    System.out.println(Thread.currentThread().getName() + "=" + i);
                }
            }
        }
    },"線程2");
    t2.start();

}

}

1.3 線程的同步

線程同步:模擬售票程序,實現三個窗口同時售票 100 張 (1.1案例)

問題:當三個窗口同時訪問共享數據時,產生了無序、重複、超額售票等多線程安全問題

解決:將須要訪問的共享數據「包起來」,視爲一個總體,確保一次只能有一個線程執行流訪問該「共享數據」

Java給上述問題提供了幾種相應的解決方法

(1).同步代碼塊

synchronized(同步監視器){

//須要訪問的共享數據

}

同步監視器 : 俗稱「鎖」,可使用任意對象的引用充當,注意確保多個線程持有同一把鎖(同一個對象)

(2).同步方法

同步方法 : 在方法聲明處加 synchronized. 注意:非靜態同步方法隱式的鎖 ---- this

例如:

public synchronized void show(){}

(3).同步鎖

同步鎖 : Lock 接口

同步代碼塊

public class SafeTicket implements Runnable{

private int ticket = 100;

@Override
public void run() {
    while(true){
        //使用同步代碼塊
        synchronized (this) {
            if(ticket > 0){
                    System.out.println(Thread.currentThread().getName() + " 完成售票,餘票:" + --ticket);
            }
        }

    }
}

}
同步方法:

public class SafeTicket implements Runnable{

private int ticket = 100;

@Override
public void run() {
    while(true){
        //使用同步代碼塊
        sale();

    }
}

public synchronized void sale(){
    if(ticket > 0){
        System.out.println(Thread.currentThread().getName() + " 完成售票,餘票:" +

--ticket);
}
}

}
同步鎖

public class SafeTicket implements Runnable{

private int ticket = 100;

private Lock l = new ReentrantLock();

@Override
public void run() {
    while(true){
        l.lock();
        try {
            if(ticket > 0){
                System.out.println(Thread.currentThread().getName() + " 完成售票,餘票:" +

--ticket);
}
} finally {
l.unlock();//釋放鎖
}

}
}

}
死鎖

死鎖 是指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於 死鎖 狀態或系統產生了 死鎖 ,這些永遠在互相等待的進程稱爲 死鎖 進程

public class TestDeadLock {
public static void main(String[] args) {
final StringBuffer s1 = new StringBuffer();
final StringBuffer s2 = new StringBuffer();

new Thread() {
        public void run() {
            synchronized (s1) {
                s2.append("A");

                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (s2) {
                    s2.append("B");
                    System.out.print(s1);
                    System.out.print(s2);
                }
            }
        }
    }.start();

    new Thread() {
        public void run() {
            synchronized (s2) {
                s2.append("C");
                synchronized (s1) {
                    s1.append("D");
                    System.out.print(s2);
                    System.out.print(s1);
                }
            }
        }
    }.start();
}

}
1.4 線程的通訊

在 java.lang.Object 類中:

wait() : 使當前「同步監視器」上的線程進入等待狀態。同時釋放鎖

notify() / notifyAll() : 喚醒當前「同步監視器」上的(一個/全部)等待狀態的線程

注意:上述方法必須使用在同步中

場景1:使用兩個線程打印 1-100 線程1和線程2交替打印

public class MyThread implements Runnable{

int i = 0;

@Override
public void run() {

    while(true){
        synchronized (this) {
            this.notify();

            if(i <= 100){
                System.out.println(Thread.currentThread().getName() + "=" + i++);
            }

            try {
                this.wait();
            } catch (InterruptedException e) {
            }
        }

    }

}

}
public class TestThread4 {
public static void main(String[] args) {
MyThread myThread = new MyThread();

Thread t1 = new Thread(myThread,"線程1");
    Thread t2 = new Thread(myThread,"線程2");

    t1.start();
    t2.start();
}

}
經典例題:生產者/消費者問題

生產者(Productor)將產品交給店員(Clerk),而消費者(Customer)從店員處取走產品,
店員一次只能持有固定數量的產品(好比:20),若是生產者試圖生產更多的產品,店員會叫生產者停一下,
若是店中有空位放產品了再通知生產者繼續生產;
若是店中沒有產品了,店員會告訴消費者等一下,若是店中有產品了再通知消費者來取走產品。
public class TestProduct {

public static void main(String[] args) {
    Clerk clerk = new Clerk();

    Productor pro = new Productor(clerk);
    Customer cus = new Customer(clerk);

    new Thread(pro).start();
    new Thread(cus).start();
}

}

// 店員
class Clerk {

private int product;

// 進貨
public synchronized void getProduct() {
    if (product >= 20) {
        System.out.println("產品已滿!");

        try {
            wait();
        } catch (InterruptedException e) {
        }

    } else {
        System.out.println("生產者生產了第" + ++product + " 個產品");

        notifyAll();
    }
}

// 賣貨
public synchronized void saleProduct() {
    if (product <= 0) {
        System.out.println("缺貨!");

        try {
            wait();
        } catch (InterruptedException e) {
        }

    } else {
        System.out.println("消費者消費了第" + --product + " 個產品");

        notifyAll();
    }
}

}

// 生產者
class Productor implements Runnable {

private Clerk clerk;

public Productor() {
}

public Productor(Clerk clerk) {
    this.clerk = clerk;
}

public Clerk getClerk() {
    return clerk;
}

public void setClerk(Clerk clerk) {
    this.clerk = clerk;
}

@Override
public void run() {
    while (true) {
        clerk.getProduct();
    }
}

}

// 消費者
class Customer implements Runnable {

private Clerk clerk;

public Customer() {
}

public Customer(Clerk clerk) {
    this.clerk = clerk;
}

public Clerk getClerk() {
    return clerk;
}

public void setClerk(Clerk clerk) {
    this.clerk = clerk;
}

@Override
public String toString() {
    return "Customer [clerk=" + clerk + "]";
}

@Override
public void run() {
    while(true){
        clerk.saleProduct();
    }
相關文章
相關標籤/搜索