资源描述
多线程技术NCEPUNorth China Etedric Fewer UnwersiyaJava2023年4月1日星期六NCEPU在煮好一杯咖啡之前,你 已经完成了很多个任务。在完成每一个任务之前和 进行下一个任务之间,都 有不同的开始、结束和执 行过程。在很多情况下,这些任务是同时发生的。生活中的事务可以 被分解执行。软件 中,就体现为多线 程。它的意义在于 一个应用程序中,有多个执行部分可 以同时执行。2023年4月1日星期六NCEPUJavaContents1.多线程基本概念2.创建线程的方式3.线程组4.线程的同步与互斥2023年4月1日星期六NCEPU4 Java8.1线程概念圜:程序、进程和多任务线程线程的生命周期与线程的状态2023年4月1日星期六NCEPU4 Java8.1线程概念圜 1程序、进程与多任务 程序(program)是对数据描述与操作的代码的集合,是 应用程序执行的脚本。进程(process)是程序的一次执行过程,是系统运行程 序的基本单位。程序是静态的,进程是动态的。系统运行 一个程序即是一个进程从创建、运行到消亡的过程。多任务(multitask)在一个系统中可以同时运行多个程 序,即有多个独立运行的任务,每个任务对应一个进程。2023年4月1日星期六NCEPU4 Java8.1线程概念圜 2线程线程(thread)就是比进程更小的运行单位,是程 序中单个顺序的流控制。一个进程中可以包含多个 线程。简单来讲,线程是一个独立的执行流,是进程内部 的一个独立执行单元,相当于一个子程序。2023年4月1日星期六NCEPU4 Java8.1线程概念圜 2线程一个进程中的所有线程都在该进程的虚拟地址空间 中,使用该进程的全局变量和系统资源。操作系统给每个线程分配不同的CPU时间片,在某 一时刻,CPU只执行一个时间片内的线程,多个时 间片中的相应线程在CPU内轮流执行。2023年4月1日星期六NCEPU8.1线程概念JavaA ProgramA Thread A Program2023年4月1日星期六NCEPUpublic class public static void main(String args)ml();)public static void ml()m2();m3();)public static void m2()public static void m3()Java8.1线程概念圜mainm2()m3()m3ml2023年4月1日星期六NCEPUpublic class TestThreadl public static void main(String args)Runnerl r=new Runnerl();r.start();for(int i=0;i100;i+)System.out.println(Main Thread:-+i);)class Runnerl extends Thread public void run()for(int i=0;i100;i+)System.out.println(Runnerl+i);)8.1线程概念JavaBThread2023年4月1日星期六NCEPUJava8.1线程概念r C:PROGRAlXINOXSlJCREAT2GE200LexeMain Thread:-0Ilunnerl:0Runnerl:1Bunnerl:2Runnerl:3Runnerl:4Runnerl:5Runnerl:6Runnerl:7Runnerl:8Main Thread:-1Runnerl:9Main Thread:-2Runnerl:10Main Thread:-3Runnerl:11Runnerl:12Main Thread:-一 一 一4Runnerl:13Main Thread:-一5Runnerl:14Main Thread:-6Runnerl:15Main Thread:-7Runnerl:162023年4月1日星期六NCEPUpublic class TestThreadl public static void main(String args)Runnerl r=new Runnerl();r.run();/r.startQ;for(int i=0;i100;i+)System.out.printlnfMain Thread:-+i);class Runnerl extends Thread public void run()for(int i=0;i Runnabl 磁口 Thread 类 两种创建线程方法的比较线程组2023年4月1日星期六NCEPU4 JavaRunnable 口 与 Thread类:为了使得一个进程在执行时,可以分解成多个线程,则程序中必须设计多个线程对象。2023年4月1日星期六NCEPUTh read类类综合了 Java中一个线程需要拥有的属性和方法。当生成一个Thread类的对象之后,就产生了一个 线程,通过该对象实例,可以启动线程、终止线 程、或者暂时挂起线程等。创建线程子类(Thread类的子类)。实现 Runnable 接 口。也要用到java Jang中定义的类Thread。2023年4月1日星期六NCEPU4 JavaRunnable 接口 Runnable接口只有一个抽象方法run。run是线程执行的起点,即在创建并启动 一个线程后,系统自动调用run方法。一个线程对象必须实现run方法,实现线程 的所有活动,已实现的run方法称为该对象 的线程体。2023年4月1日星期六NCEPU4 JavaThread 类 Thread类实现了接口 Runnable,将run方法实现为空方法,并定义了多个操作线程的方法。Thread的构造方法 public Th read(ThreadGroup group,Runnable target,String name)g rou p指明了线程所属的线程组;target是线程体run。方法所在的对象,它必须实现接 口Runnable的run方法。为空时,由对象本身执行线 程体。name是线程的名称。为空时,系统提供一个名字。2023年4月1日星期六NCEPU4 JavaThread 类圜 Thread类的类方法 public static Thread currentThread()返回当前执行线程的引用对象。public static int activeCount()返回当前线程组中活动线程个数。public static int enumerate(Thread tar ray)将当前线程组中的活动线程拷贝至lltarray数组中,包括子线程。2023年4月1日星期六NCEPU4 JavaThread 类圜 Thread类的实例方法 public final String getNameQ 返回线程名。public final void setName(String name)设置线程的名字。public void start()启动已创建的线程对象 public final boolean isAlive。返回线程是否启动的状态 public final ThreadGroup getThreadGroup()返回当前线程所属的线程组名。Public String toString()返回线程的字符串信息,包括名字、优先级和线程组。2023年4月1日星期六NCEPU4 Java多线程实现圜定义线程的操作,即定义线程的run。方法。在适当时候创建线程对象。启动该线程对象。2023年4月1日星期六NCEPU4 Java创建一个线程圜创建一个线程就是创建一个新的虚拟CPU(virtualCPU),让它去运行一段代码。1 定义一个类,实现Runnable接口,并overrid run()方法,在这个方法里是你希望这个线程运行的代码。2创建一个新类的对象。3创建一个Thread类的对象,用刚才的Runnable对象 作为构造函数参数。-4调用Thread对象的start。方法来启动线程。2023年4月1日星期六public class SimpleRunnable implements Runnable private String message;public static void main(String args)SimpleRunnable rl=new SimpleRunnable(nHellon);Thread tl=new Thread(rl);tlstart();for(;)System.out printin(11 Bye-bye11);!,public SimpleRunnable(String message)this.message=message;)public void run()for(;)System.out.printin(message);2023年4月1日星期六NCEPUJC:WINNTsystem32cmd.exeBye-bye Bye-bye Bye-bye Bye-bye Bye-bye Bye-bye Bye-bye Bye-bye Bye-bye HelloHello Hello Hello Hello Hello Hello Hello Bye-bye Bye-bye Bye-bye Bye-bye Bye-bye Bye-bye Bye-bye-11 x|H2023年4月1日星期六NCEPU4 JavaRunnable 接 口 与 Thread 类圜Runnable 接口Runnable接口中只声明了一个run方法。public void run()run是线程执行的起点。Th read类Thread类将Runnable接口中的run方法实现为空方法。2023年4月1日星期六NCEPU4 JavaRunnable 接 口 与 Thread 类圜:Thread类用于创建可控制线程。就像应用程序必须从main。方法开始执行一样,一个线程必须从run。方 法开始执行,而run。方法声明在Runnable接口中2023年4月1日星期六NCEPUJava创建线程的方式方法一:继承Thread类创建线程ru以:持步骤:1定义一个类ExR继承Thread类2覆盖run。方法(此方法中写你想要同时执行的那 件事)3 main。中new几个ExR类的对象tl,t2.4 start这些对象2023年4月1日星期六NCEPUpublic class TestThreadl public static void main(String args)Runnerl r=new Runnerl();r.start();for(int i=0;i100;i+)System.out.println(Main Thread:-+i);)class Runnerl extends Thread public void run()for(int i=0;i:);while(ijava Threadlact iueCount=4Threadl:1 3 5 7 9 11 13 15 17 19 21 23 25 2?Thvead2:2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38|40 42 44 46 48 Thread2 end?29 31 33 35 37 39 41 43 45 47 49 Threadl end!MjL:516java ThreadlactiueCount=4Threadl:1 3 5 7 9 11 13 15 17 19 21Thread2:2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38|40 42 44 46 48 Thread2 end?23 25 27 29 31 33 35 37 39 41 43 45 47 49 Threadl end?|L:516jaua Threadlact iueCount=4Threadl:1 3 5 7 9 11 13 15 17 19 21 23 25 27I微软拼音半:_ _52023年4月1日星期六NCEPUJava例继承Thread类创建线程。图三个级程在运行2023年4月1日星期六NCEPU4 Java继承Thread类创建线程的特点国 直接操作线程,使用简单方便。适用于单重继承,不能再继承其他类。如果必须有另一个父类,该方法不合适。可以使用实现Runnable接口的方法。2023年4月1日星期六NCEPUJava创建线程的方式圜步骤:1定义一个类ExR实现Runnable接口2覆盖run。方法(此方法中写你想要同时执行的那 在;件事)象,3 main。中new几个ExR的对象tl,t2.法 4 main。中用H,t2作为构造方法的参数,new几个Thread类的对象5 start这些对象2023年4月1日星期六NCEPUpublic class TestThreadl public static void main(String args)Runnerl r=new Runnerl();Thread t=new Thread(r);t.start();for(int i=0;i100;i+)Sclass Runnerl implements Runnable)public void run()for(int i=0;i100;i+)System.out.println(Runnerl+i);)Java例实现Runnable接口创建线程public class Runnablel implements Runnable(int k=0;public Runnablel(int k)(this.k=k;public void run()(int i=k;System.out.println();while(i0)System.out.println(Thread.currentThread(),getName()+is saling ticket+tickets);)Java案例的实现注意:一个线程对象只能启动一个线程,无论你调用多少遍start。方法,结果都只有一个线程2023年4月1日星期六NCEPUpublic class Th read 1 public static void main(String args)new TestTh read().start();new TestThread().start();new TestTh read().start();new TestThread().start();)class TestThread extends Thread int tickets=100;public void run()while(tickets0)System.out.println(Thread.currentThread(),getName()+is saling ticket+tickets-);)2023年4月1日星期六NCEPU4 Java案例的实现修改:在main方法中创建四个ThreadTest对象继续运行这个程序可以看到每个票号都被打印了四遍,即四个线程各自 卖各自的100张票,这是因为创建了ThreadTest对象,等于创建了四 个资源,每个对象都有100张票,每个线程在独立的处理各自的资源。结论:要实现这个铁路售票模拟程序,只能创建一个资源对象(该对象中包 含要发售的一百张票),但要创建多个线程去处理同一个资源对象,并 且每个线程上所运行的是相同的代码。这只能通过接口来实现。2023年4月1日星期六NCEPU4 Java案例的实现我们用Runnable接口来实现铁路售票程序。2023年4月1日星期六NCEPUpublic class Th read 1 public static void main(String args)TestThread tt=new TestThread();new Thread(tt).start();new Th read(tt),start。;new Thread(tt).start();new Th read(tt),start。;)class TestThread implements Runnable int tickets=100;public void run()while(tickets0)System.out.println(Thread.currentThread(),getName()+is saling ticket tickets-);)案例的实现量JavaC:PRCX3R A 1 X I FXJOXS 1 实现了 4个线程Thread-3is Thread-。is Thread-1 is Thrad-2is Thread-3is Thrad-0is Thread-1 is Thiead-Nia Thread-3is Thread-Ois Thread-1 is Thread-2is Thread-3issa 1 sal sa 1ea 1 sa 1 sa 1 sal sa 1 sa 1 sa 1 sa 1 sa 1 salmg ing i ng ing i ng ing ing ing ing ing ing ing i na时 同7。W B F 1 1987654321 J ttttttttt we eeeeeeeee kkkkkkkkkkkkk票 张 O上面的程序中,创建了四个线程,每个线程调用同一个ThreadTest 对象中的run。方法,访问同一个对象中的变量(tickets)的实例,正确的解决了问题。弱立3年4月,六4 Java两种创建方式的比较:直接继承Thread类。该方法编写简单,可以直接操作 线程,适用于单重继承情况,因而不能再继承其他类:实现Runnable接口。当一个线程已继承了另一个类 时,就只能用实现Runnable接口的方法来创建线程。2023年4月1日星期六NCEPU4 Java3线程组:每个线程都是一个线程组的成员,线程组把多个线程集成为一 个对象,通过线程组可以同时对其中的多个线程进行操作(如:启动一个线程组的所有线程)Java的线程组由Java.lang包中的ThreadGroup类实现。ThreadGroup类用来管理一组线程,包括:线程的数目,线程间的关系,线程正在执行的操作,以及线程将要启动或终止 时间等。2023年4月1日星期六NCEPU4 Java线程组圜创建线程组 ThreadGroup g=new ThreadGroup(groupName);将各个线程添加给该线程组 Thread t=new Th read(g,th read Na me);要中断某个线程组中所有线程的运行 g.interrupt。;要确定线程组中的任一线程是否仍处于可运行状态 if(g.activeCountO=0)2023年4月1日星期六NCEPU4线程的生命周期与线程的状态Running运行年人导致阻塞 的事件2023年4月1日星期六NCEPUJava8.1线程概念线程的生命周期与线程的状态2023年4月1日星期六NCEPU线程的生命周期与线程的状态New创建 一start。一Running运行年人suspendQ wait()sleep()I/Oblocking()2023年4月1日星期六NCEPUJava线程的生命周期及控制线程是程序内部的一个顺序控制流,它具有一个特定 的生命周期。在一个线程的生命周期中,它总处于某一种 状态中。线程的状态表示了线程正在进行的活动以及在这 段时间内线程能完成的任务。2023年4月1日星期六NCEPUJava线程的生命周期及控制创建状态(new Thread)当创建了一个新的线程时(myThread thd=new myThreadQi),它就处于创建状态,此时它仅仅是一个空的 线程对象,系统不为它分配资源。处于这种状态时只能启动或终 止该线程,调用除这两种以外的其它方法都会失败并且会引起非 法状态例外IHegalThreadStateException(对于其它状态,若所调用的方法与状态不符,都会引起非法状态例外)。2023年4月1日星期六NCEPUJava线程的生命周期及控制注意:这一状态并不是运行中状态(Running),因为线程也 许实际上并未真正运行。很多计算机都是单处理器的,所以要 在同一时刻运行所有的处于可运行状态的线程是不可能的,TJava运行系统必须实现调度来保证这些线程共享处理器。但是;在大多数情况下,可运行状态也就是运行中,当一个线程正在(.运行时,它是可运行的,并且也是当前正运行的线程。资源:安排其后行:并调用线程体run()方法:这样就使得该线程处于可运行(Runnable)状态。2023年4月1日星期六NCEPUJava线程的生命周期及控制运行中状态(Running)Java运行系统通过调度选中一个Runnable的线程,使其 占有CPU并转为运行中状态(Running),此时,系统真正 执行线程的run。方法。2023年4月1日星期六NCEPUJava线程的生命周期及控制阻塞状态(Blocked)或称不可运行状态(Not Runnable)一个正在运行的线程因某种原因不能继续运行时,进入阻 塞状态,线程处于可运行状态时,当下面四种情况发生,线程 就进入不可运行状态:调用了sleep。方法;调用了suspend。方法;为等候一个条件变量,线程调用wait。方法;输入输出流中发生线程阻塞。注意:处于阻塞状态的线程是不能执行的,即使处理器空闲,也不能 执行,只有当引起阻塞的原因被消除一Runnable2023年4月1日星期六NCEPUJava线程的目注意:每种方法都仅仅对相应的情况才有作用,不可运行状态(N调用resume。方法是无效的,并且还会引起非 对于这四种使得法状态例外。法使线程返回可运行状态:如果线程处于睡眠状态中,sleep。方法中的参数为休息时 间,当这个时间过去后,线程即为可运行的;A如果一个线程被挂起,须调用resume。方法来返回;如果线程在等待条件变量,那么要停止等待的话,需要该条件变量所在的对象调用notify。或notifyAII。方法;如果在1/0流中发生线程阻塞,则特定的I/O指令将结束这 种不可运行状态。2023年4月1日星期六NCEPUJava线程的生命周期及控制死亡状态(Dead)线程的终止一般可通过两种方法实现:自然撤消或是被停 止。自然撤消是指从线程的run。方法正常退出;而调用线程的实例方法stop。则可以强制停止当前线程。线程自然撤销public void run()int i=0;while(i 100)i+;System.out.println(vvi=+i);线程被强行停止myThread thd=new MyThread();thd.start();trythd.sleep(lOOOO);catch(InterruptedException e)thd.stop();2023年4月1日星期六NCEPU线程调度模型独占方式:当前执行线程将一直执行下去,直到执行完毕或由于某种原因主动放弃CPU,或CPU被一个更高 优先级的线程抢占。分时方式:当前运行线程获得一个时间片,时间到时,即使没有执行完也要让出CPU,进入可运行状态,等 待下一个时间片的调度。系统选中其他可运行状态的线 程执行。2023年4月1日星期六NCEPU4 Java线程调度与优先组10为最高级,1级别最低,优先级 默认值为5线程的优先级用工数字表示,默认值是5。public final int getPriority()获得线程的优先级public final void setPriority(int newPriority)设定线程的优先级2023年4月1日星期六NCEPUJava线程控制的基本方法方法功能isAlive()判断线程是否还“活”着,即线程是否还未终止。getPriorityO获得线程的优先级数值。setPriority()设置线程的优先数值。Thread.sleep()将当前线程睡眠制定毫秒数。joinQ调用某线程的该方法,将当前线程与该线程“合 并”,即等待该线程结束,再恢复当前线程的运行。Thread-yieldQ让出CPU,当前线程进入就绪队列等待调度。wait()当前线程进入对象的wait poolinterrupt()方法为线程设置一个中断标记notify/notifyAII()唤醒对象的wait pool中的一个/所有等待线程2023年4月1日星期六NCEPUJava改变线程状态 start():启动线程 Thread.sleepQ:线程休眠Thread.yieldQ:暂停线程的执行,线程让步。为优先级大于等于自己的线程让 步,否则不做任何操作。Thread.currentThread():返回正在运行的Thread对象。interruptQ:中断睡眠中线程的方法。stop():终止线程suspend。:将线程挂起,暂停运行,但系统不破坏线程的执行环境,可以用 resu me()来恢复本线程的执行。resume。:恢复被挂起的线程进入可运行状态。join():等待其他线程结束。2023年4月1日星期六NCEPU4 Javasleep和yield方法的区别圜 Lsleep不考虑优先级的让步;yield为优先级大于等于自 己的线程让步。Zsleep将转到阻塞状态;yield转到就绪状态。(阻塞要 经过就绪,才能运行)3.sleep需要显示的捕获或抛出异常;yield方法没有声明 抛出任何异常。当前运行线程可以调用另一个线程的join方法,当前运行 线程转到阻塞状态,直到另一个线程结束,才会恢复运行。2023年4月1日星期六NCEPUJava改变线程状态stop()1public class SimpleRunnable implements Runnable private String message;publicIstatic void main(String args)SimpleRunnable rl=new SimpleRunnableHello);Thread tl=new Thread(rl);tl.start();for(int i=0;i65536;i+)System.out.printin(nBye-bye11);)Thread|.sleep(1000);tl.stopO;publicSimpleRunnable(String message)this.message=message;)publicvoid run()for(;)2023年4月1日星期六NCEPUJava改变线程状态圜 sleep。方法:可以暂停线程的执行,让其它线程得到机会。但sleep。要抛出异常InterruptedException,该异常必须 捕获。try sleep(lOO)catch(InterruptedException e).本异常可不作处理2023年4月1日星期六NCEPUimport java.util.*;public class Testinterrupt public static void main(String args)MyThread thread=new MyThread();thread.start();try Thread.sleep(lOOOO);catch(InterruptedException e)thread.interrupt();)class MyThread extends Thread boolean flag=true;public void run()while(flag)System.out.println(=+new Date()+=);try sleep(lOOO);catch(InterruptedException e)return;)4 Java改变线程状态圜 suspend。方法和resume。可以用来暂停线程或恢复线程。可以由线程自身在线程体中调用suspend。方法暂停自己,也可以在其他线程中通过线程实例调用。suspend。方法暂停线程的执行。但是要恢复由suspend。方法暂停的线程,只能在其他线程中通过线程实例调用 resume。方法。2023年4月1日星期六NCEPUJava例线程sleep国public class Thread2 extends Thread(public Thread2(ThreadGroup tg,String name)(super(tg,name);public void run()(try(System.out.print(getName()+sleep);this.sleep(1000);睡眠一秒钟|catch(lnterruptedException e)2023年4月1日星期六NCEPUJava线程的基本控制一 Thread类提供如下基本线程控制方法:心static void main(String1 args)FirstThread first=new FirstThreadf);SecondThread second=new SecondThreadO;first.start();second.start();try(System.out.printin(11 Waiting for first thread to finish.n);连E吃曲曲?j&一 _,_ _system.out.printin(1111Ts a long wait!);ISys tern.out.printin(11 Waking up second thread.J);second.resume();System.out.printin(nWaiting for second thread to finish.n);second.joint);catch(InterruptedException e)System.out.printin(111Tm ready to finish too.11);运中().2023年4月1日星期六 ixictrupublic class TestJoin public static void main(String args)MyThread2 tl=new MyThread2(abcde);tl.startQ;try tljoin。;catch(InterruptedException e)for(int i=l;i0)tryThread.sleep(10);catch(Exception e)System.out.println(Th read.currentThread(),getNa me()+is saling ticket+tickets-);2023年4月1日星期六NCEPU4 Java多线程的互斥举例程序运行后,屏幕上打出的最后几行结果如下:Thread-2 is saling ticket 3Thread-3 is saling ticket 2Th read-4 is saling ticket 1Thread-1 is saling ticket 0Thread-2 is saling ticket-1Thread-3 is saling ticket-2打印出负数,表示同一张票被卖了4次的意外发生。2023年4月1日星期六NCEPUpublic class TestSync implements Runnable(Timer timer=new Timer();public static void main(String args)TestSync test=new TestSync();Thread t1=new Thread(test);Thread t2=new Thread(test);tl.setNameCtl);t2.setName(t2);t1.start();t2.start();public void run()timer.add(Thread.currentThread().getName();2023年4月1日星期六NCEPUJavac:命令提示符D:javac TestSync.javaD:java TestSynctl,你是第2个使用timer的线程 t2,你是第2个使用timer的线程D:2023年4月1日星期六NCEPU例银行账户的存取款线程设计class Accountl(private String name;private int value;void put(int i)(value=value+i;int get(int i)(if(valuei)value=value-i;else(i=value;value=0;账户缓冲区欲存入金额i存入时,value值增加欲取金额i,返回实际取到金额取走时,value值减少账户金额不够所取时取走全部所余金额2023年4月1日星期六NCEPU4 Java互斥锁为解决操作的不完整性问题,在Java语言中,引入了对 象互斥锁的概念,来保证共享数据操作的完整性。在Java语言中,为保证线程对共享资源操作的完整性,用关键字synchronized为共享资源加锁来解决这个问题。这个锁使得共享资源对线程是互斥操作的,称为互斥锁。2023年4月1日星期六NCEPU4 Java共享数据的线程“互斥”锁定线程间的数据共享synchronized可修饰一个代码块或一个方法,使修饰对象 在任一时刻只能有一个线程访问。锁定一段代码 锁定一个方法synchronized(this)synchronized 方法声明如果synchronized用在类声明中,:则表明该类中的所有方法都是synchronized的U2023年4月1日星期六NCEPUJava改进售票系统:方法一:同步代码块:方法二:同步函数2023年4月1日星期六NCEPUJava例带锁定的存取款线程设计class Save2 extends Thread 存款线程(private Accountl a1;private int amount;public Save2(Account1 a1 Jnt amount)(this.al=a1;this.amount=amount;public void run()(synchronized(a1)锁定账户对象(int k=a1.howmatch();in,2023年4月1日星期六NCEPU线程间传送数据Bufferputgetvalue2023年4月1日星期六NCEPUJava例发送线程与接收线程。class Bufferlprivate int value;void put(int i)value=i;int get()return value;class Senderl extends Thread缓冲区发送线程2023年4月1日星期六NCEPU*synchronized与“互斥锁”标志如果将Bufferl类中的put和get方法声明为“互斥”方法:synchronized void put(int i)synchronized int get()2023年4月1日星期六NCEPU4 Java传送数据的线程同步运行圜在线程之间通信A wait。方法使当前线程变为阻塞状态,主动释放互斥锁,并 进入该互斥锁的等待队列。A notify。方法唤醒等待队列中的其他线程继续执行,使这些 线程变为可运行状态。iwtifyAII。方法唤醒等待队列中的全部线程继续执行。2023年4月1日星期六NCEPUJava例同步的发送线程与接收线程Bufferput value空时,放置新值,唤醒 va山e不空时,等待isEmptyvaluevalue空时,
展开阅读全文