《Java多线程编程核心技术》学习笔记(二)——多线程间通信

线程间通信

等待/通知机制

方法wait()的作用是使当前执行代码的线程进行等待,wait()方法是Object类的方法,该方法用来将当前线程置入”预执行队列”中,并且在wait()所在的代码行处停止执行,直到接到通知或被中断为止。在调用wait()之前,线程必须获取该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法。在执行wait()方法后,当前线程释放锁。在从wait()返回前,线程与其他线程竞争重新获得锁,如果调用wait()时没有持有适当的锁,则抛出IllegalMonitorStateException

方法notify()也要在同步方法或同步块中调用,即在调用前,线程也必须获得该对象的对象级别锁,如果调用notify()时没有持有适当的锁,也会抛出IllegalMonitorStateException。该方法用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则由线程规划器随机挑选出其中一个呈wait状态的线程,对其发出通知notify,并使它等待获取该对象的对象锁。需要说明的是,在执行notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也并不能马上获取该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出synchronized代码块后,当前线程才会释放锁,而呈wait状态所在的线程才可以获取该对象锁。当第一个获得了该对象锁的wait线程运行完毕以后,它会释放掉该对象锁,此时如果该对象没有再次使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,还会继续阻塞在wait状态,直到这个对象发出一个notify或notifyAll。

wait使线程停止运行,而notify使停止的线程继续运行

    synchronized(lock){
        System.out.println("开始   wait time="+System.currentTimeMillis());
        lock.wait();
        System.out.println("结束   wait time="+System.currentTimeMillis());
    }

    ...

    synchronized(lock){
        System.out.println("开始   notify time="+System.currentTimeMillis());
        lock.notify();
        System.out.println("结束   notify time="+System.currentTimeMillis());
    }

释放锁的情况:

  • 1)执行完同步代码块就会释放对象的锁
  • 2)在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放
  • 3)在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程会释放对象锁,而此线程对象会进入线程等待池中,等待被唤醒

方法wait(long)的使用

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

生产者/消费者模式实现

    //生产者
    public class P{

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

        public void setValue(){
            try{
                synchronized(lock){
                    while(!ValueObject.value.equals("")){
                        System.out.println("生产者"+Thread.currentThread().getName+" WAITING了");
                        lock.wait();
                    }
                    System.out.println("生产者"+Thread.currentThread().getName()+" RUNNABLE了");
                    String value = System.currentTimeMillis()+"_"+System.nanoTime();
                    ValueObject.value = value;
                    lock.notify();
                }
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }

    }

    //消费者
    public class C{

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

        public void getValue(){
            try{
                synchronized(lock){
                    while(ValueObject.value.equals("")){
                        System.out.println("消费者"+Thread.currentThread().getName+" WAITING了");
                        lock.wait();
                    }
                    System.out.println("消费者"+Thread.currentThread().getName()+" RUNNABLE");
                    ValueObject.value = "";
                    lock.notify();
                }
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }

    }

    //线程P
    public class ThreadP extends Thread{

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

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

    }

    //线程C
    public class ThreadC extends Thread{

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

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

    }

    //执行
    public class Run{

        public static void main(String[] args) throws InterruptedException{

            String lock = new String("");
            P p = new P(lock);
            C c = new C(lock);
            ThreadP[] pThread = new ThreadP[2];
            ThreadC[] cThread = new ThreadC[2];
            for(int i = 0;i<2;i++){
                pThread[i] = new ThreadP(p);
                pThread[i].setName("生产者"+(i+1));
                cThread[i] = new ThreadC(c);
                pThread[i].setName("消费者"+(i+1));
                pThread[i].start();
                cThread[i].start();
            }
            Thread.sleep(5000);
            Thread[] threadArray = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
            Thread.currentThread().getThreadGroup().enumerate(threadArray);
            for(int i =0;i<threadArray.length;i++){
                System.out.println(threadArray[i].getName()+""+threadArray[i].getState());
            }

        }

    }

多生产与多消费导致假死:notify唤醒的不是异类而是同类,比如”生产者”唤醒”生产者”或”消费者”唤醒”消费者”,就会导致所有的线程都不能继续运行下去,都呈WAITING状态

解决:用notifyAll()代替notify()

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

管道流(pipeStream)是一种特殊的流,用于在不同线程间直接传送数据。一个线程发送数据到输出管道,另一个线程从输出管道中读取数据。从而实现不同线程间的通信

在Java的JDK中提供了4个类来使线程间可以进行通信:

  • 1)PipedInputStream和PipedOutputStream
  • 2)PipedReader和PipedWriter

    //WriteData类
    public 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();
            }
        }
    
    }
    
    //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();
            }
        }
    
    }
    
    //Write线程
    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);
        }
    
    }
    
    //Read线程
    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);
        }
    
    }
    
    //Run类
    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.connnect(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();
            }
        }
    
    }
    

    使用inputStream.connect(outputStream)或outputStream.connect(inputStream)的作用使两个Stream之间产生通信链接,这样才可以将数据进行输出与输入

    管道中还可以传递字符流,只要将PipedInputStream换成PipedReader,PipedOutputStream换成PipedWriter

多线程join的使用

在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程往往将早于子线程结束之前结束。如果主线程想等到子线程执行完成后再结束,就要用到join()方法,
join()方法的作用是使所属的线程对象x正常执行run()方法中的任务,而使当前线程z进行无限期的阻塞,等待线程x销毁后再继续执行线程z后面的代码

方法join具有使线程排队运行的作用,有些类似同步的运行效果。join与synchronized的区别是:join在内部使用wait()方法进行等待,而synchronized关键字使用的是”对象监视器”原理做为同步

在join过程中,如果当前线程对象被中断,则当前线程出现异常(join()和interrupt()彼此遇到就会出现异常)

join(long)中的参数是设定等待的时间

join(long)与sleep(long)的区别:
join(long)的功能在内部是使用wait(long)方法来实现的,所以join(long)方法具有释放锁的特点。
而Thread.sleep(long)方法却不释放锁

类ThreadLocal的使用

变量值的共享可以使用public static变量的形式,所有的线程都使用同一个public static变量,如果想实现每一个线程都有自己的共享变量可以使用类ThreadLocal

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

    public class ThreadLocalExt extends ThreadLocal{

        //覆盖initialValue()方法具有初始值
        @Override
        protected Object initialValue(){
            return new Date().getTime();
        }

    }

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

    //线程A
    public class ThreadA extends Thread{

        @Override
        public void run(){
            try{
                for(int i = 0;i<10;i++){
                    System.out.println("在ThreadA线程中取值="+Tools.t1.get());
                    Thread.sleep(100);
                }
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }

    }

    //Run类
    public class Run(){

        public static void main(String[] args){
            try{
                for(int i = 0;i<10;i++){
                    System.out.print("   在Main线程中取值="+Tools.t1.get());
                    Thread.sleep(100);
                }
                Thread.sleep(5000);
                ThreadA a = new ThreadA();
                a.start();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值