线程基础知识

Posted 罗贱人

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程基础知识相关的知识,希望对你有一定的参考价值。

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多线程编程核心技术

如果有什么错误欢迎指出来,感谢感谢!才学疏浅望谅解。

以上是关于线程基础知识的主要内容,如果未能解决你的问题,请参考以下文章

线程基础四 使用Monitor类锁定资源

Java——线程池

newCacheThreadPool()newFixedThreadPool()newScheduledThreadPool()newSingleThreadExecutor()自定义线程池(代码片段

Java线程池详解

Java线程池详解

Java 线程池详解