1. 程式人生 > >線程基礎知識(三)

線程基礎知識(三)

thrown 但是 runnable take 停止 reader RR min tcp

wait、notify、notifyAll

wait、sleep、yield區別?

sleep 讓出cpu使用權但是不會釋放鎖。

public class Test {
public static void main(String[] args) {
    Object obj=new Object();
    ThreadOne one=new ThreadOne(obj);
    ThreadTwo two=new ThreadTwo(obj);
    one.start();
    two.start();
}
}

class ThreadOne extends Thread{
private Object obj;
public ThreadOne(Object obj){
    this.obj=obj;
}
@Override
public void run(){
    synchronized (obj) {
        System.out.println(Thread.currentThread().getName());
        for (int i =0;i<5;i++) {
            System.out.println("  "+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
}

class ThreadTwo extends Thread{
private Object obj;
public ThreadTwo(Object obj){
    this.obj=obj;
}
@Override
public void run(){
    synchronized (obj) {
        System.out.println(Thread.currentThread().getName());
        for (int i =0;i<5;i++) {
            System.out.println("  "+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
}

技術分享圖片

wait 只能在同步方法或方法塊中調用(即需要獲取對象的鎖),wait會讓出cpu使用和對象鎖

當線程呈wait()狀態是,調用線程對象的interrupt()方法會出現InterruptedException異常。

新手的坑

public class Mistake {
public static void main(String[] args) {
    Object object=new Object();
    MyThreadThree threadThree=new MyThreadThree(object);
    threadThree.start();
}
}

class MyThreadThree extends Thread{
private Object object;
public MyThreadThree(Object object){
    this.object=object;
}

@Override
public void run(){
    synchronized (object){
        for (int i=1;i<5;i++){
            try {
                wait();//坑點
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
}

技術分享圖片

坑點:wait() 需要調用鎖對象.wait() 上面代碼的wait()是線程對象的wait 所以拋出IllegalMonitorStateException,它是RuntimeException的一個子類不需要try-catch進行捕捉異常。

object.wait();//將wait()改為object.wait()就正確了

notify、notifyAll 也需要在同步方法或方法塊中調用(即需要獲取對象的鎖) 調用notify或notifyAll後並不會立即釋放鎖需要將程序執行完即退出synchronized代碼塊後。

notify和notifyAll的區別就是notify喚醒一個wait的線程使其的狀態變為Runnable(可運行)狀態,notifyAll是喚醒所有wait的線程

public class Test {
public static void main(String[] args) {
    Service service=new Service();
    ThreadOne one=new ThreadOne(service);
    ThreadTwo two=new ThreadTwo(service);
    one.start();
    two.start();
}
}

class Service{
private volatile boolean flag =true;

public boolean getFlag(){
    return flag;
}
public void setFlag(boolean flag){
    this.flag=flag;
}
}


class ThreadOne extends Thread{
private Service service;
public ThreadOne(Service service){
    this.service=service;
}
@Override
public void run(){
    synchronized (service) {
        while (true){
            if(service.getFlag()==true){
                try {
                    System.out.println("堵塞了One");
                    service.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                System.out.println(Thread.currentThread().getName()+"One");
                service.setFlag(true);
                service.notifyAll();
            }
        }
    }
}
}

class ThreadTwo extends Thread{
private Service service;
public ThreadTwo(Service service){
    this.service=service;
}
@Override
public void run(){
    synchronized (service) {
        while (true){
            if(service.getFlag()==false){
                try {
                    System.out.println("堵塞了 Two");
                    service.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                System.out.println(Thread.currentThread().getName()+"Two");
                service.setFlag(false);
                service.notifyAll();
            }
        }
    }
}
}

技術分享圖片

yield 會讓出cpu使用,但是不會釋放對象鎖。

public class TestYield {
public static void main(String[] args) {
    Object object=new Object();
    YieldThread yieldThread=new YieldThread(object);
    yieldThread.start();
    YieldThread yieldThreadTwo=new YieldThread(object);
    yieldThreadTwo.start();
}
}

class YieldThread extends Thread{
private Object object;
public YieldThread(Object object){
    this.object=object;
}
@Override
public void run(){
    synchronized (object) {
        for (int i = 1; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "   " + i);
            Thread.yield();
        }
    }
}
}

技術分享圖片

public class TestYield {
public static void main(String[] args) {
    YieldThread yieldThread = new YieldThread();
    yieldThread.start();
    YieldThread yieldThreadTwo = new YieldThread();
    yieldThreadTwo.start();
}
}

class YieldThread extends Thread {
@Override
public void run() {
    for (int i = 1; i < 5; i++) {
        System.out.println(Thread.currentThread().getName() + "   " + i);
        Thread.yield();
    }

}
}

技術分享圖片


總結

  1. wait 讓出cpu使用讓出對象鎖
  2. sleep、yield 讓出cpu使用但是不讓出對象鎖
  3. wait、notify、notifyAll 只能在同步方法或方法塊中調用(即需要獲取對象的鎖)不然會拋出IllegalMonitorStateException

技術分享圖片


notify、notifyAll

先看看一個生產者多個消費者

public class TestCP {
public static void main(String[] args) {
    List<String> storeHouse = new ArrayList<>(0);
    Consumer consumer = new Consumer(storeHouse);
    Producer producer = new Producer(storeHouse);
    for (int i = 0; i < 1; i++) {
        new ProducerThread(producer).start();
    }

    for (int i = 0; i < 20; i++) {
        new ConsumerThread(consumer).start();
    }

}
}

class Consumer {
//廠庫
private List<String> storeHouse;

public Consumer(List<String> storeHouse) {
    this.storeHouse = storeHouse;
}

//消費
public void consume() {
    synchronized (storeHouse) {
        while (storeHouse.size() == 0) {
            try {
                System.out.println("沒有蛋糕了.....");
                storeHouse.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //消費第一個
        storeHouse.remove(0);
        storeHouse.notify();
    }
}
}

class ConsumerThread extends Thread {
private Consumer consumer;

public ConsumerThread(Consumer consumer) {
    this.consumer = consumer;
}

@Override
public void run() {
    while (true) {
        consumer.consume();
    }
}
}

class Producer {
//廠庫
private List<String> storeHouse;

public Producer(List<String> storeHouse) {
    this.storeHouse = storeHouse;
}

//生產
public void produce() {
    synchronized (storeHouse) {
        //當廠庫裏面蛋糕數據到10就就停止生產
        while (storeHouse.size() == 10) {
            try {
                System.out.println("蛋糕有10塊了");
                storeHouse.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //生產
        storeHouse.add("蛋糕");
        storeHouse.notify();
    }
}
}

class ProducerThread extends Thread {
private Producer producer;

public ProducerThread(Producer producer) {
    this.producer = producer;
}

@Override
public void run() {
    while (true) {
        producer.produce();
    }
}
}

技術分享圖片

堵塞了..... why???

  1. 因為有20個消費者蛋糕數量只有10個,比如當有15個消費者等待
  2. 當生產者生產的蛋糕數量到達10個時,wait
  3. 還有5個消費者消費notify喚醒的是等待的消費者,當消費完10個蛋糕後notify還是消費者那麽這樣,所有的消費者都進入wait狀態而生產者也是wait狀態所以堵塞了。

將notify改為notifyAll後就可以正確的執行。、

storeHouse.notifyAll();

建議使用notifyAll


線程間通信:管道

一個線程發數據到輸出管道,另一個線程從輸入管道中讀取數據。

字節流

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

public class Run {
public static void main(String[] args) {
    try {
        WriteData writeData = new WriteData();
        ReadData readData = new ReadData();
        PipedInputStream inputStream = new PipedInputStream();
        PipedOutputStream outputStream = new PipedOutputStream();
        //inputStream.connect(outputStream)
        outputStream.connect(inputStream);
        ThreadRead threadRead = new ThreadRead(readData,inputStream);
        threadRead.start();
        Thread.sleep(2000);
        ThreadWrite threadWrite=new ThreadWrite(writeData,outputStream);
        threadWrite.start();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

class WriteData {
public void writeMethod(PipedOutputStream out) {
    try {
        System.out.println("write :");
        for (int i = 0; i < 300; i++) {
            String outData = "" + (i + 1);
            out.write(outData.getBytes());
            System.out.print(outData);
        }
        System.out.println();
        out.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
}

class ThreadWrite extends Thread {
private WriteData write;
private PipedOutputStream out;

public ThreadWrite(WriteData write, PipedOutputStream out) {
    this.write = write;
    this.out = out;
}

@Override
public void run() {
    write.writeMethod(out);
}
}


class ReadData {
public void readMethod(PipedInputStream input) {
    try {
        System.out.println("read :");
        byte[] byteArray = new byte[20];
        int readLength = input.read(byteArray);
        while (readLength != -1) {
            String newData = new String(byteArray, 0, readLength);
            System.out.print(newData);
            readLength = input.read(byteArray);
        }
        System.out.println();
        input.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
}

class ThreadRead extends Thread {
private ReadData read;
private PipedInputStream input;

public ThreadRead(ReadData read, PipedInputStream input) {
    this.read = read;
    this.input = input;
}

@Override
public void run() {
    read.readMethod(input);
}
}

inputStream.connect(outputStream)或outputStream.connect(inputStream)使兩個Stream之間產生通信鏈接


字符流

import java.io.*;
public class Run {
public static void main(String[] args) {
    try {
        WriteData writeData = new WriteData();
        ReadData readData = new ReadData();
        PipedReader reader = new PipedReader();
        PipedWriter writer = new PipedWriter();
        //writer.connect(reader);
        reader.connect(writer);
        ThreadRead threadRead = new ThreadRead(readData,reader);
        threadRead.start();
        Thread.sleep(2000);
        ThreadWrite threadWrite=new ThreadWrite(writeData,writer);
        threadWrite.start();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

class WriteData {
public void writeMethod(PipedWriter out) {
    try {
        System.out.println("write :");
        for (int i = 0; i < 300; i++) {
            String outData = "" + (i + 1);
            out.write(outData);
            System.out.print(outData);
        }
        System.out.println();
        out.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
}

class ThreadWrite extends Thread {
private WriteData write;
private PipedWriter out;

public ThreadWrite(WriteData write, PipedWriter out) {
    this.write = write;
    this.out = out;
}

@Override
public void run() {
    write.writeMethod(out);
}
}


class ReadData {
public void readMethod(PipedReader input) {
    try {
        System.out.println("read :");
        char[] byteArray = new char[20];
        int readLength = input.read(byteArray);
        while (readLength != -1) {
            String newData = new String(byteArray, 0, readLength);
            System.out.print(newData);
            readLength = input.read(byteArray);
        }
        System.out.println();
        input.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
}

class ThreadRead extends Thread {
private ReadData read;
private PipedReader input;

public ThreadRead(ReadData read, PipedReader input) {
    this.read = read;
    this.input = input;
}

@Override
public void run() {
    read.readMethod(input);
}
}

join

如果現在有兩個線程一個線程想等另外一個線程運行結束後再運行那麽就可以使用join。

public class TestJoin {
public static void main(String[] args) {
    MyThreadJoin join=new MyThreadJoin();
    join.start();
    try {
        join.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName()+" 我在等他結束後運行!!!");
}
}

class MyThreadJoin extends Thread{
@Override
public void run(){
    System.out.println("sleep..........");
    try {
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

技術分享圖片

join源碼使用wait()方法實現————也就說join會讓出cpu使用和對象鎖

/**
 * Waits at most {@code millis} milliseconds for this thread to
 * die. A timeout of {@code 0} means to wait forever.
 *
 * <p> This implementation uses a loop of {@code this.wait} calls
 * conditioned on {@code this.isAlive}. As a thread terminates the
 * {@code this.notifyAll} method is invoked. It is recommended that
 * applications not use {@code wait}, {@code notify}, or
 * {@code notifyAll} on {@code Thread} instances.
 *
 * @param  millis
 *         the time to wait in milliseconds
 *
 * @throws  IllegalArgumentException
 *          if the value of {@code millis} is negative
 *
 * @throws  InterruptedException
 *          if any thread has interrupted the current thread. The
 *          <i>interrupted status</i> of the current thread is
 *          cleared when this exception is thrown.
 */
public final synchronized void join(long millis)
throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

join讓出對象鎖

public class TestJoin {
public static void main(String[] args) {
    ThreadB b=new ThreadB();
    ThreadA a=new ThreadA(b);
    a.start();
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    ThreadC c=new ThreadC(b);
    c.start();
}
}

class ThreadA extends Thread{
private ThreadB threadB;
public ThreadA(ThreadB threadB){
    this.threadB=threadB;
}

@Override
public void  run(){
    try{
        synchronized (threadB){
            threadB.start();
            Thread.sleep(6000);
        }
    }catch (InterruptedException e){
        e.printStackTrace();
    }
}
}

class ThreadB extends Thread{
@Override
public void run(){
    try {
        System.out.println("ThreadB run begin time" + System.currentTimeMillis());
        Thread.sleep(5000);
        System.out.println("ThreadB run End time"+System.currentTimeMillis());
    }catch (InterruptedException e){
        e.printStackTrace();
    }
}
public synchronized void bService(){
    System.out.println("bService time"+System.currentTimeMillis());
}
}

class ThreadC extends Thread{
private ThreadB threadB;
public ThreadC(ThreadB threadB){
    this.threadB=threadB;
}

@Override
public void run(){
    threadB.bService();
}
}

技術分享圖片

將ThreadA的run()方法改為

 threadB.join();
//Thread.sleep(6000);

技術分享圖片


Java多線程編程核心技術

如果有什麽錯誤歡迎指出來,感謝感謝!才學疏淺望諒解。

線程基礎知識(三)