深入理解Java多线程(三)

关于java多线程的概念以及基本用法:java多线程基础

3, 线程间通信

线程在操作系统中是独立的个体,经过特殊的处理,线程间可以实现通信,进而成为一个整体,提高CPU利用率

3.1,等待/通知机制

等待:wait()方法作用是使当前执行代码的线程阻塞,在调用wait()之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法。执行wait()方法后,当前线程会释放锁。如果调用wait()方法时没有锁,那么会抛出IllegalMonitorStateException

通知:notify()也要在同步方法或者同步代码块中调用,调用之前,线程也必须获得该对象的对象级别的锁,若没有,则会抛出IllegalMonitorStateException。该方法用于通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则由线程规划器随机挑选其中一个呈wait状态的线程,对其发出通知,并使它等待获取该对象的对象锁。执行notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也不能马上获取该对象锁,要等执行notify()方法的线程执行完,当前线程才会释放锁。
方法notifyAll()可以唤醒全部线程

新建MyThread1类:

public class MyThread1 extends Thread{
    private Object lock;

    public MyThread1(Object lock) {
        super();
        this.lock = lock;
    }

    @Override
    public void run() {
        try {
            synchronized(lock) {
                System.out.println("开始  wait time"+System.currentTimeMillis());
                lock.wait();
                System.out.println("结束  wait time"+System.currentTimeMillis());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

新建MyThread2类:

public class MyThread2 extends Thread{
    private Object lock;

    public MyThread2(Object lock) {
        super();
        this.lock = lock;
    }

    @Override
    public void run() {
            synchronized(lock) {
                System.out.println("开始  notify time"+System.currentTimeMillis());
                lock.notify();
                System.out.println("结束  notify time"+System.currentTimeMillis());
            }
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        try {
            Object lock = new Object();
            MyThread1 t1 = new MyThread1(lock);
            t1.start();
            Thread.sleep(3000);
            MyThread2 t2 = new MyThread2(lock);
            t2.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

结果:

开始  wait time1534469153084
开始  notify time1534469156084
结束  notify time1534469156084
结束  wait time1534469156084

线程t1在执行wait()方法后进入阻塞状态,3秒后线程t2执行,执行了notify()方法后线程t2在执行完代码块后唤醒线程t1

方法wait(time)的作用是等待一段时间是否有线程对锁进行唤醒,如果超过这个时间就自动唤醒

public class MyRunnable {
    static private Object lock = new Object();
    static private Runnable runnable1 = new Runnable() {

        @Override
        public void run() {
            try {
                synchronized(lock) {
                System.out.println("wait begin timer="+System.currentTimeMillis());
                lock.wait(5000);
                System.out.println("wait end timer="+System.currentTimeMillis());
                } 
            }catch (Exception e) {
                e.printStackTrace();
            }

        }
    };

    public static void main(String[] args) {
        Thread t=  new Thread(runnable1);
        t.start();
    }
}

结果:

wait begin timer=1534473901885
wait end timer=1534473906886
3.2,通过管道进行线程间通信:字节流

Java语言里提供了很多的输入/输出流Stream,便于数据读写,pipeStream(管道流)是一种特殊的流,用于在不同线程间直接传送数据,一个线程发送数据到输出管道,另一个线程从输入管道中读取数据,可以实现线程间的通信
Java的JDK提供了4个类

  • 字节流:PipedInputStream和PipedOutPutStream
  • 字符流:PipedReader和PipedWriter

先看看字节流:
新建WriteData类:

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

新建ReadData类:

public 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();
        }
    }

}

新建两个线程:

public class ThreadWrite extends Thread{

    private WriteData write;
    private PipedOutputStream out;

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

    @Override
    public void run() {
        write.writeMethod(out);
    }
}
public class ThreadRead extends Thread{

    private ReadData read;
    private PipedInputStream input;

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

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

测试类:

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 e1) {
            e1.printStackTrace();
        }
    }
}

结果:

read: 
write: 
1234567891011121314151617181920
1234567891011121314151617181920

再看看字符流:
WriteData类:

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

ReadData类:

public 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();
        }
    }

}

两个线程类:

public class ThreadRead extends Thread{

    private ReadData read;
    private PipedReader input;

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

    @Override
    public void run() {
        read.readMethod(input);
    }
}
public class ThreadWrite extends Thread{

    private WriteData write;
    private PipedWriter out;

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

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

测试类:

public class Run {

    public static void main(String[] args) {
        try {
            WriteData writeData = new WriteData();
            ReadData readData = new ReadData();

            PipedReader inputStream =new PipedReader();
            PipedWriter outputStream = new PipedWriter();

            //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 e1) {
            e1.printStackTrace();
        }
    }
}

结果:

read: 
write: 
1234567891011121314151617181920
1234567891011121314151617181920
3.2,方法join的使用

很多时候,主线程创建并启动子线程,子线程要进行一些耗时的计算,主线程往往早于子线程结束,如果主线程想等待子线程结束,则需要join()方法

新建MyThread类:

public class MyThread extends Thread{

    @Override
    public void run() {
        try {
            int secondValue = (int)(Math.random()*1000);
            System.out.println(secondValue);
            Thread.sleep(secondValue);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

测试类:

public class Test {

    public static void main(String[] args) {
        try {
            MyThread threadTest = new MyThread();
            threadTest.start();
            threadTest.join();
            System.out.println("我要等子线程结束再开始");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

结果:

231
我要等子线程结束再开始

join()方法的作用是使所属线程对象正常执行run()方法,使当前主线程处于阻塞状态,等子线程结束后再执行主线程,join在内部使用wait()方法进行等待

方法join(time)和sleep(time)的区别?
这两个方法都会等待time时间再运行,但是在同步的处理上是不一样的
join(time)的功能是在内部使用wait(time)方法,所以join方法其实会释放锁,
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;
            }
        }
    }

在执行join(time)方法后,当前线程的锁被释放,此时其他线程可以调用此线程的同步方法,而sleep(time)方法则不会释放锁

3.3,类ThreadLocal的使用

ThreadLocal为变量在每个线程中都创建了一个副本,所以每个线程都可以访问自己内部的副本变量

新建Tools类:

public class Tools {

    public static ThreadLocal t1 = new ThreadLocal();
}

两个线程类:

public class ThreadA extends Thread{

    @Override
    public void run() {
        try {
            for(int i=0;i<10;i++) {
                Tools.t1.set("ThreadA"+(i+1));
                System.out.println("ThreadA get Value="+Tools.t1.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }   
}
public class ThreadB extends Thread{

    @Override
    public void run() {
        try {
            for(int i=0;i<10;i++) {
                Tools.t1.set("ThreadB"+(i+1));
                System.out.println("ThreadB get Value="+Tools.t1.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }   
}

测试类:

public class Run {

    public static void main(String[] args) {
        try {
            ThreadA a = new ThreadA();
            ThreadB b = new ThreadB();
            a.start();
            b.start();
            for(int i=0;i<10;i++) {
                Tools.t1.set("Main"+(i+1));
                System.out.println("Main get Value="+Tools.t1.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果:

Main get Value=Main1
ThreadA get Value=ThreadA1
ThreadB get Value=ThreadB1
ThreadB get Value=ThreadB2
ThreadA get Value=ThreadA2
Main get Value=Main2
Main get Value=Main3
ThreadB get Value=ThreadB3
ThreadA get Value=ThreadA3
Main get Value=Main4
ThreadB get Value=ThreadB4
ThreadA get Value=ThreadA4
ThreadB get Value=ThreadB5
ThreadA get Value=ThreadA5
Main get Value=Main5
Main get Value=Main6
ThreadA get Value=ThreadA6
ThreadB get Value=ThreadB6
ThreadB get Value=ThreadB7
ThreadA get Value=ThreadA7
Main get Value=Main7
ThreadB get Value=ThreadB8
Main get Value=Main8
ThreadA get Value=ThreadA8
Main get Value=Main9
ThreadA get Value=ThreadA9
ThreadB get Value=ThreadB9
Main get Value=Main10
ThreadB get Value=ThreadB10
ThreadA get Value=ThreadA10

由结果可以看到有虽然有3个线程set()数据值但是每个线程都能取出属于自己的数据

3.4,类InheritableThreadLocal的使用

使用InheritableThreadLocal类可以在子线程中取得父线程继承下来的值
如果在子线程取得值的同时,主线程将InheritableThreadLocal中的值进行更改,那么子线程取到的值还是旧值

  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值