Java多线程编程核心技术--第3章 线程间通信

3.1 等待、通知机制

3.1.3 等待/通知机制的实现

wait()方法作用是让当前线程进行等待,wait是Object类的方法,用来将当前线程置入“预执行队列”中。在wait所在代码行处停止执行,直到接到通知或被中断为止。

调用wait()之前,线程必须获得该对象的对象级别锁,也就是只能在同步方法或同步块中调用wait()方法。
如果调用wait时线程没有持有适当的锁,则抛出IllegalMonitorStateException,它是RuntimeException的子类,不需要try-catch。

notify()方法也要在同步方法或同步块中调用,也需要线程的对象级别锁。
用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则由线程规划器随机挑出一个wait状态的线程,对其notify,让该线程得到对象锁。

执行notify()方法后,当前线程不会马上释放该对象锁,要等执行notify()方法的线程将程序执行完,退出synchronized代码块后,当前线程才会释放锁。

使线程等待

//这样会报错,没有对象监视器,也就是同步加锁
public class Test {
    public static void main(String[] args){
        try{
            String str = new String("");
            str.wait();
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
}

public class Test {
    public static void main(String[] args){
        try{
            String lock = new String();
            synchronized(lock){
                System.out.println("syn第一行");
                lock.wait();   
                System.out.println("syn第二个");
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
}

通知线程,使线程继续运行下去

public class Test {
    public static void main(String[] args) {
        Object lock = new Object();     //需要是同一个对象锁
        ThreadA a= new ThreadA(lock);
        a.start();
        ThreadB b =new ThreadB(lock);
        b.start();
    }
}

class ThreadA extends Thread{
    private Object lock;
    public ThreadA(Object lock){
        this.lock = lock;
    }
    public void run(){
        try {
            synchronized (lock){
                System.out.println("开始 wait time:" + System.currentTimeMillis());
                lock.wait();
                System.out.println("结束 wait time:" + System.currentTimeMillis());
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

class ThreadB extends Thread{
    private Object lock;
    public ThreadB(Object lock){
        this.lock = lock;
    }
    public void run(){
        synchronized (lock){
            System.out.println("开始 notify time:" + System.currentTimeMillis());
            lock.notify();
            System.out.println("结束 notify time:" + System.currentTimeMillis());
        }
    }
}

/*
开始 wait time:1525527916728
开始 notify time:1525527916729
结束 notify time:1525527916729
结束 wait time:1525527916729
*/

当线程被wait后,如果再被notify,代码就从wait()方法之后继续执行。

notifyAll()方法:可以唤醒所有正在等待队列中的同一共享资源的全部线程,进入可运行状态,优先级最高的线程最先执行,但也可能是随机执行。

3.1.4 方法wait锁释放和notify锁不释放

方法wait被执行后,锁被自动释放;执行完notify方法,锁不自动释放。

public class Test {
    public static void main(String[] args) {
        Object lock = new Object();
        ThreadA a = new ThreadA(lock);
        a.start();
        NotifyThread notifyThread = new NotifyThread(lock);
        notifyThread.start();
        SynNotifyMethodThread synNotifyMethodThread = new SynNotifyMethodThread(lock);
        synNotifyMethodThread.start();
    }
}

class Service {
    public void testWaitMethod(Object lock){
        try{
            synchronized (lock){
                System.out.println("begin wait thread name:"+Thread.currentThread().getName());
                lock.wait();
                System.out.println("end wait thread name:"+Thread.currentThread().getName());
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    public void synNotifyMethod(Object lock){
        try{
            synchronized (lock){
                System.out.println("begin notify thread name:"+Thread.currentThread().getName());
                lock.notify();
                Thread.sleep(5000);
                System.out.println("end notify thread name:"+Thread.currentThread().getName());
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

class ThreadA extends Thread {
    private Object lock;
    public ThreadA(Object lock){
        super();
        this.lock = lock;
    }
    public void run(){
        Service service= new Service();
        service.testWaitMethod(lock);
    }
}

class NotifyThread extends Thread{
    private Object lock;
    public NotifyThread(Object lock){
        super();
        this.lock = lock;
    }
    public void run(){
        Service service= new Service();
        service.synNotifyMethod(lock);
    }
}

class SynNotifyMethodThread extends Thread{
    private Object lock;
    public SynNotifyMethodThread(Object lock){
        super();
        this.lock = lock;
    }
    public void run(){
        Service service= new Service();
        service.synNotifyMethod(lock);
    }
}

/*
begin wait thread name:Thread-0
begin notify thread name:Thread-1
end notify thread name:Thread-1
end wait thread name:Thread-0

begin notify thread name:Thread-2
end notify thread name:Thread-2
*/

必须执行完notify方法所在的同步synchronized代码块后才释放锁。

3.1.5 interrupt方法和wait方法

线程呈wait()状态,调用interrupt()方法会出现InterruptedException异常。

class Service {
    public void testWaitMethod(Object lock){
        try{
            synchronized (lock){
                System.out.println("begin wait thread name:"+Thread.currentThread().getName());
                lock.wait();
                System.out.println("end wait thread name:"+Thread.currentThread().getName());
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

class ThreadA extends Thread {
    private Object lock;
    public ThreadA(Object lock){
        super();
        this.lock = lock;
    }
    public void run(){
        Service service= new Service();
        service.testWaitMethod(lock);
    }
}

public class Test {
    public static void main(String[] args) {
        Object lock = new Object();
        ThreadA a = new ThreadA(lock);
        a.start();
        Thread.sleep(1000);
        a.interrupt();
    }
}

3.1.8 方法wait(long)的使用

wait(long)方法功能是等待某一时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒。

//5秒内自动唤醒
public class MyRunnable {
    static private Object lock = new Object();
    static private Runnable runnable = new Runnable(){
        public void run(){
            try{
                synchronized(lock){
                    System.out.println("wait begin time="+System.currentTimeMillis());
                    lock.wait(5000);
                    System.out.println("wait end time="+System.currentTimeMillis());
                }
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args){
        Thread t = new Thread(runnable);
        t.start();
    }
}

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

3.1.11.1 一生产、一消费:

//存储值对象
class ValueObject{
    public static String value;
}

//生产者
class Produce {
    private String lock;
    public Produce(String lock){
        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的值是:"+value);
                ValueObject.value = value;
                lock.notify();
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
}

//消费者
class Consumer {
    private String lock;
    public Consumer(String lock){
        this.lock = lock;
    }

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

3.1.12 管道进行线程通信:字节流

用于不同线程间的通信

PipedInputStream和PipedOutputStream

public class Test {
    public static void main(String[] args) throws Exception {
        WriteData writeData = new WriteData();
        ReadData readData = new ReadData();

        PipedInputStream inputStream = new PipedInputStream();
        PipedOutputStream outputStream = new PipedOutputStream();
        //outputStream.connect(inputStream);
        inputStream.connect(outputStream);
        ReadThread readThread = new ReadThread(readData,inputStream);
        readThread.start();
        WriteThread writeThread= new WriteThread(writeData,outputStream);
        writeThread.start();
    }
}

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 ReadData {
    public void readMethod(PipedInputStream in) {
        try {
            System.out.println("read:");
            byte[] bytes = new byte[20];
            int readLength = in.read(bytes);
            while (readLength != -1){
                String newData = new String(bytes, 0, readLength);
                System.out.print(newData);
                readLength = in.read(bytes);
            }
            System.out.println();
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

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

    public WriteThread(WriteData write,PipedOutputStream out){
        super();
        this.write = write;
        this.out = out;
    }
    public void run(){
        write.writeMethod(out);
    }
}

class ReadThread extends Thread {
    private ReadData read;
    private PipedInputStream in;

    public ReadThread(ReadData read,PipedInputStream in){
        super();
        this.read = read;
        this.in = in;
    }
    public void run(){
        read.readMethod(in);
    }
}

3.1.13 管道进行线程通信:字符流

PipedWrite和PipedReader

代码同上

3.1.14 实战:等待/通知交叉备份

创建20个线程,10个线程将数据备份到A数据库中,另外10个线程将数据备份到B数据库中,备份A和B是交叉进行的。

public class Test {
    public static void main(String[] args) {
        DBTools dbTools = new DBTools();
        for (int i = 0; i < 20; i++) {
            ThreadA a = new ThreadA(dbTools);
            a.start();
            ThreadB b = new ThreadB(dbTools);
            b.start();
        }
    }
}

class DBTools {
    volatile private boolean preIsA = false;

    synchronized public void backupA() {
        try {
            while (preIsA == true) {
                wait();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println("********");
            }
            preIsA = true;
            notifyAll();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    synchronized public void backupB() {
        try {
            while (preIsA == false) {
                wait();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println("==========");
            }
            preIsA = false;
            notifyAll();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class ThreadA extends Thread {
    private DBTools dbTools;

    public ThreadA(DBTools dbTools) {
        super();
        this.dbTools = dbTools;
    }

    public void run() {
        dbTools.backupA();
    }
}

class ThreadB extends Thread {
    private DBTools dbTools;

    public ThreadB(DBTools dbTools) {
        super();
        this.dbTools = dbTools;
    }

    public void run() {
        dbTools.backupB();
    }
}

3.2 join()使用

主线程创建并启动子线程,如果子线程要进行大量耗时运算,主线程可能会早于子线程结束前结束。

如果这时主线程想等子线程执行完后再结束,并去子线程中取数据。就要用到join。

作用是等待线程对象销毁。

clas MyThread extends Thread {
    public void run(){
        try{
            int secondValue = (int)(Math.random() * 1000);
            //sleep的值不确定
            System.out.println(secondValue);
            Thread.sleep(secondValue);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
}

public class Test {
    public static void main(String[] args){
        try{
            MyThread my = new MyThread();
            my.start();
            my.join();
            //如果没有join,这句话会先于secondValue输出
            System.out.println("我想当MyThread对象执行完后再执行!");
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
}

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

join具有使线程排队运行的作用,类似于同步效果。

join和synchronized区别是:
join在内部用wait()方法进行等待,而synchronized使用的是“对象监视器”原理作为同步。

3.2.4 join(long)的使用

参数是设置等待的时间

class MyThread extends Thread {
    @Override
    public void run(){
        try{
            System.out.println("begin time="+System.currentTimeMillis());
            Thread.sleep(5000);     //原本是五秒
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

public class Test {
    public static void main(String[] args){
        try{
            MyThread my = new MyThread();
            ny.start();
            my.join(2000);  //只等2秒
            //Thread.sleep(2000);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

3.2.5 join(long)和sleep(long)的区别

join(long)的功能在内部是使用wait(long)来实现的,所以join具有释放锁的特点。

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;
        }
    }   
}

3.3 类ThreadLocal的使用

变量值得共享可以用public static 声明的形式,所有线程都使用同一个public static变量。

如果每个线程都有自己的共享变量如何解决?用ThreadLocal

ThreadLocal主要解决的是每个线程绑定自己的值,可将ThreadLocal类比成全局存放数据的盒子,盒子中可存储每个线程的私有数据

3.3.1 get()和null

public class Run{
    public static ThreadLocal t1 = new ThreadLocal();
    public static void main(String[] args){
        if(t1.get() == null){
            System.out.println("从未放过值");
            t1.set("我的值");
        }
        System.out.println(t1.get());
        System.out.println(t1.get());
    }
}

多个线程同时调用ThreadLocal,测试数据隔离性

public class Test {
    public static void main(String[] args) throws Exception {
        ThreadA a = new ThreadA();
        a.start();
        Thread.sleep(1000);
        ThreadB b = new ThreadB();
        b.start();
    }
}

class Tools {
    public static ThreadLocal<Date> t1 = new ThreadLocal<Date>();
}

class ThreadA extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 20; i++) {
                if (Tools.t1.get() == null) {
                    Tools.t1.set(new Date());
                }
                System.out.println("A " + Tools.t1.get().getTime());
                Thread.sleep(100);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class ThreadB extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 20; i++) {
                if (Tools.t1.get() == null) {
                    Tools.t1.set(new Date());
                }
                System.out.println("B " + Tools.t1.get().getTime());
                Thread.sleep(100);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.3.3 解决第一次get()返回null的问题

public class Test {
    public static ThreadLocalExt t1 = new ThreadLocalExt();
    public static void main(String[] args) {
        System.out.println(t1.get());
    }
}

//继承ThreadLocal
class ThreadLocalExt extends ThreadLocal {
    @Override
    protected Object initialValue(){
        return "我是默认值";
    }
}

3.4 类InheritableThreadLocal的使用

该类可以在子线程中取得父线程继承下来的值。

3.4.1 用InheritableThreadLocal让子线程从父线程中取值

public class Test {
    public static void main(String[] args) {
        try {
            for(int i=0;i<10;i++){
                System.out.println("在主方法中取值="+Tools.t1.get());
                Thread.sleep(100);
            }
            Thread.sleep(5000);
            ThreadA a = new ThreadA();
            a.start();
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

class InheritableThreadLocalExt extends InheritableThreadLocal {
    @Override
    protected Object initialValue() {
        return new Date().getTime();
    }

    //注意!这里的值可以用在子线程中
    @Override
    protected Object childValue(Object parentValue) {
        return parentValue + "我在子线程中加的";
    }
}

class Tools {
    public static InheritableThreadLocalExt t1 = new InheritableThreadLocalExt();
}

class ThreadA extends Thread{
    @Override
    public void run() {
        try {
            for(int i=0;i<10;i++){
                System.out.println("在线程A中取值="+Tools.t1.get());
                Thread.sleep(100);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
/*
在主方法中取值=1525754211326
在主方法中取值=1525754211326
在主方法中取值=1525754211326
在主方法中取值=1525754211326
在主方法中取值=1525754211326
在线程A中取值=1525754211326我在子线程中加的
在线程A中取值=1525754211326我在子线程中加的
在线程A中取值=1525754211326我在子线程中加的
在线程A中取值=1525754211326我在子线程中加的
在线程A中取值=1525754211326我在子线程中加的
*/

猜你喜欢

转载自blog.csdn.net/a464700300/article/details/80237928