多线程之线程间通信

一、wait/notify机制

(拥有相同锁的线程才可以实现wait/notify机制)

原理
wait():是Object类的方法,它的作用是使当前执行wait()方法的线程等待,在wait()所在的代码行处暂停执行,并释放锁,直到接到通知或中断为止。在执行wait()之前,线程必须获得该对象的对象级锁,即只能在同步方法或同步代码块中调用wait()。(调用wait()若没有持有锁,则抛出IlegalMonitorStateException)
notify()同样需要在同步方法或同步代码块中调用。 该方法用来通知那些可能等待该锁的线程(若有多个线程等待,按wait()方法执行顺序进行通知)。执行notify()后,不会马上释放该锁,要在线程将程序执行完(退出synchronized区域)才会释放。

notifyAll()
唤醒全部线程
需要注意的是:唤醒的顺序取决于JVM的实现。

wait(long time)
等待指定时间time后,接着往下执行
想要自动往下接着执行的话也需要持有锁,如果没有锁的话就会一直等待

线程的状态切换
在这里插入图片描述
JDK8中对于Thread状态的枚举定义,所有的状态如下所示:
NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED

1)NEW
一个已经创建的线程,但是还没有调用start方法启动的线程所处的状态
2)RUNNABLE
该状态包含两种可能。有可能正在运行,或者正在等待CPU资源。总体上就是当我们创建线程并且启动之后,就属于Runnable状态。
3)BLOCKED
阻塞状态,当线程准备进入synchronized同步块或同步方法的时候,需要申请一个监视器锁而进行的等待,会使线程进入BLOCKED状态。
4)WAITING
该状态的出现是因为调用了Object.wait()或者Thread.join()或者LockSupport.park()。处于该状态下的线程在等待另一个线程 执行一些其余action来将其唤醒。
5)TIMED_WAITING
该状态和上一个状态其实是一样的,是不过其等待的时间是明确的。
6)TERMINATED
消亡状态比较容易理解,那就是线程执行结束了,run方法执行结束表示线程处于消亡状态了

消费者/生产者模式的实现

一生产一消费:操作值
在这里插入图片描述
存储值的对象

public class ValueObject {

    public static String value = "";
    
}

生产者

public class P {

    private String lock;

    public P(String lock) {
        super();
        this.lock = lock;
    }

    public void setValue() {
        try {
            synchronized (lock) {
                if (!ValueObject.value.equals("")) {
                    lock.wait();
                }
                String value = System.currentTimeMillis() + "_" + System.nanoTime();
                System.out.println("set is :" + value);
                ValueObject.value = value;
                lock.notify();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

消费者

public class C {

    private String lock;

    public C(String lock) {
        super();
        this.lock = lock;
    }

    public void getValue() {
        try {
            synchronized (lock) {
                if (ValueObject.value.equals("")) {
                    lock.wait();
                }
                System.out.println("get is :" + ValueObject.value);
                ValueObject.value = "";
                lock.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

生产者线程

public class ThreadP extends Thread {

    private P p;

    public ThreadP(P p) {
        super();
        this.p = p;
    }


    @Override
    public void run() {
        while (true) {
            p.setValue();
        }
    }
}

消费者线程

public class ThreadC extends Thread {

    private C c;

    public ThreadC(C c) {
        super();
        this.c = c;
    }

    @Override
    public void run() {
        while (true) {
            c.getValue();
        }
    }
}

测试代码

public class Test {

    public static void main(String[] args) {
        String lock = new String("");
        P p = new P(lock);
        C c = new C(lock);

        ThreadP threadP = new ThreadP(p);
        ThreadC threadC = new ThreadC(c);

        threadP.start();
        threadC.start();
    }
}

在这里插入图片描述
需要注意的是:当有多个生产者和多个消费者的时候,可能会出现“假死”(也就是所有的线程都呈waiting状态,不再执行任何任务)
因为notify()唤醒的未必是异类,可能生产者唤醒了生产者,消费者唤醒了消费者
解决假死:可将notify()改为notifyAll()

多生产多消费(一次生产/消费多个值)
创建容器(最大容量10)

public class Box {

    private static List list = new ArrayList();

    synchronized public void add() {
        if (size() < 50) {
            list.add("anything");
            System.out.println("Thread: " + Thread.currentThread().getName() + "execute add(), " +
                    "the size is :" + size());
        }
    }

    synchronized public Object popFirst() {
        Object obj = list.remove(0);
        System.out.println("Thread: " + Thread.currentThread().getName() + "execute popFirst(), " +
                "the size is : " + size());
        return obj;
    }

    synchronized public int size() {
        return list.size();
    }
}

生产者业务代码

public class SetService {

    private Box box;

    public SetService(Box box) {
        super();
        this.box = box;
    }

    public void setMethod() {
        try {
            synchronized (this) {
                while (box.size() == 10) {
                    System.out.println("---------------");
                    this.wait();
                }
                Thread.sleep(300);
                box.add();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void checkBoxStatus() {
        try {
            while (true) {
                synchronized (this) {
                    if (box.size() < 50) {
                        this.notifyAll();
                    }
                }
                System.out.println("set checkbox = " + box.size());
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

生产者线程代码

public class SetValueThread extends Thread {

    private SetService setService;

    public SetValueThread(SetService setService) {
        super();
        this.setService = setService;
    }

    @Override
    public void run() {
        while (true) {
            setService.setMethod();
        }
    }
}

生产者容器大小测试类

public class SetCheckThread extends Thread {

    private SetService setService;

    public SetCheckThread(SetService setService) {
        super();
        this.setService = setService;
    }

    @Override
    public void run() {
        setService.checkBoxStatus();
    }
}

消费者业务代码

public class GetService {

    private Box box;

    public GetService(Box box) {
        super();
        this.box = box;
    }

    public void getMethod() {
        try {
            synchronized (this) {
                while (box.size() == 0) {
                    System.out.println("||||||||||||||");
                    this.wait();
                }
                box.popFirst();
            }
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void checkBoxStatus() {
        try {
            while (true) {
                synchronized (this) {
                    if (box.size() > 0) {
                        this.notifyAll();
                    }
                }
                System.out.println("get checkbox = " + box.size());
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

消费者线程代码

public class GetValueThread extends Thread {

    private GetService getService;

    public GetValueThread(GetService getService) {
        super();
        this.getService = getService;
    }

    @Override
    public void run() {
        while (true) {
            getService.getMethod();
        }
    }
}

消费者容器大小测试类

public class GetCheckThread extends Thread {

    private GetService getService;

    public GetCheckThread(GetService getService) {
        super();
        this.getService = getService;
    }

    @Override
    public void run() {
        getService.checkBoxStatus();
    }
}

测试代码

public class Test {

    public static void main(String[] args) throws InterruptedException {
        Box box = new Box();
        SetService setService = new SetService(box);

        for (int i = 0; i < 2; i++) {
            SetValueThread setValueThread = new SetValueThread(setService);
            setValueThread.start();
        }

        Thread.sleep(50);
        SetCheckThread setCheckThread = new SetCheckThread(setService);
        setCheckThread.start();

        Thread.sleep(10000);

        GetService getService = new GetService(box);
        for (int i = 0; i < 10; i++) {
            GetValueThread getValueThread = new GetValueThread(getService);
            getValueThread.start();
        }
        Thread.sleep(50);
        GetCheckThread getCheckThread = new GetCheckThread(getService);
        getCheckThread.start();
    }
}

测试结果
在这里插入图片描述
在这里插入图片描述

通过管道进行线程间的通信
java语言提供了各种各样的输入/输出流,其中管道流是一种特殊的流,用在不同线程间直接传送数据。
字节流:PipedInputStream、PipedOutputStream
字符流:PipedReader、PipedWriter

join()方法

很多情况下,主线程创建并启动子线程,如果子线程要进行大量的耗时运算,主线程可能会在子线程结束之前结束,如果想要主线程等待子线程结束后再结束,那么此时就需要join()(内部使用了wait(),当前线程会释放锁)

作用:使所属线程对象A正常执行run()方法中的任务,而使当前线程B进入无限期的阻塞。等待A执行完销毁后再继续执行B后面的代码

join(long time):参数用于设定等待时间,不管使用该方法的线程是否执行完毕,时间到了就会去重新获得锁并接着执行。

join(long millis, int nanos):等待该线程终止的时长为 millis(毫秒) + nanos(纳秒)

类ThreadLocal

作用:将数据放入当前线程对象中的Map中,这个Map是Thread的实例变量。类ThreadLocal自己部管理、不存储任何数据,它只是数据和Map之间的桥梁,用于将数据放入Map中(每个线程的Map存有自己的数据,每个Thread的Map值对当前线程可见),如图
在这里插入图片描述
如果从未在Thread中的Map存储ThreadLocal对象对应的value值,则get()返回null

隔离性的验证
在这里插入图片描述
ThreadLocal变量

public class Tools {

    public static ThreadLocal t1 = new ThreadLocal();

}

线程A

public class MyThreadA extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                Tools.t1.set("A " + (i + 1));
                System.out.println("A get " + Tools.t1.get());
                int sleepValue = (int) (Math.random() * 1000);
                Thread.sleep(sleepValue);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

线程B

public class MyThreadB extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                Tools.t1.set("B " + (i + 1));
                System.out.println("    B get " + Tools.t1.get());
                int sleepValue = (int) (Math.random() * 1000);
                Thread.sleep(sleepValue);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

测试类

public class Test {

    public static void main(String[] args) throws Exception {
        MyThreadA a = new MyThreadA();
        MyThreadB b = new MyThreadB();
        a.start();
        b.start();
        for (int i = 0; i < 10; i++) {
            Tools.t1.set("main " + (i + 1));
            System.out.println("              main get" + Tools.t1.get());
            int sleepValue = (int) (Math.random() * 1000);
            Thread.sleep(sleepValue);
        }
    }

}

测试结果
在这里插入图片描述
由上可知,通过ThreadLocal可向每一个线程存储自己的私有数据,虽然三个线程都向t1进行set(),但是都能取出自己set的值

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值