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

来源:互联网 发布:软件测试团队 编辑:程序博客网 时间:2024/06/05 05:08

线程间通信

等待/通知机制

方法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
原创粉丝点击