资源描述
第第8章章 多线程多线程8.1 多线程的概念8.2 线程类8.3 资源的协调与同步8.4 线程间通信8.1 多线程的概念多线程的概念程序是一段静态的代码,它是应用软件执行的蓝本。进程就是程序的运行时的一个实例。线程可以看作单独地占有CPU时间来执行相应的代码的。线程是共享地址空间的,也就是说多线程可以同时读取相同的地址空间,并且利用这个空间进行交换数据。8.1 多线程的概念多线程的概念多线程具有以下特点:(1 1)多个线程在运行时,系统自动在线程之间进行切换;)多个线程在运行时,系统自动在线程之间进行切换;(2 2)由于多个线程共存于同一块内存,线程之间的通信非常容易;)由于多个线程共存于同一块内存,线程之间的通信非常容易;(3 3)JavaJava将线程视为一个对象。线程要么是将线程视为一个对象。线程要么是ThreadThread类的对象,要么是接类的对象,要么是接口口RunnableRunnable的对象。的对象。(4 4)当多个线程并行执行时,具有较高优先级的线程将获得较多的)当多个线程并行执行时,具有较高优先级的线程将获得较多的CPUCPU时间片;时间片;(5 5)优先级是从)优先级是从0 0到到1010的整数,并且它仅表示线程之间的相对关系;的整数,并且它仅表示线程之间的相对关系;(6 6)多个线程共享一组资源,有可能在运行时产生冲突。必须采用)多个线程共享一组资源,有可能在运行时产生冲突。必须采用synchronizedsynchronized关键字协调资源,实现线程同步。关键字协调资源,实现线程同步。8.2 线程类线程类8.2.1 多线程编程中常用的常量和方法8.2.2 线程的生命周期8.2.3 创建多线程的方法8.2.1 多线程编程中常用的常量和方法多线程编程中常用的常量和方法Thread类包含的常量有:1.1.public static final int MAX_PRIORITYpublic static final int MAX_PRIORITY:最大最大优先级,值是优先级,值是1010。2.2.public static final int MIN_PRIORITYpublic static final int MIN_PRIORITY:最小最小优先级,值是优先级,值是1 1。3.3.public static final int NORM_PRIORITYpublic static final int NORM_PRIORITY:缺省:缺省优先级,值是优先级,值是5 5。8.2.1 多线程编程中常用的常量和方法多线程编程中常用的常量和方法常用方法:currentThread()currentThread():返回当前运行的线程对象,是一个静态的方返回当前运行的线程对象,是一个静态的方法。法。sleep(int n)sleep(int n):使当前运行的线程睡使当前运行的线程睡n n个毫秒,然后继续执行,个毫秒,然后继续执行,也是静态方法。也是静态方法。yield()yield():使当前运行的线程放弃执行,切换到其它线程,是一:使当前运行的线程放弃执行,切换到其它线程,是一个静态方法。个静态方法。isAlive()isAlive():判断线程是否处于执行的状态,返回值判断线程是否处于执行的状态,返回值truetrue表表示处于运行状态,示处于运行状态,falsefalse表示已停止。表示已停止。start()start():使调用该方法的线程开始执行。:使调用该方法的线程开始执行。run()run():该方法由:该方法由start()start()方法自动调用。方法自动调用。8.2.1 多线程编程中常用的常量和方法多线程编程中常用的常量和方法常用方法:stop()stop():使线程停止执行,并退出可执行状态。:使线程停止执行,并退出可执行状态。suspend()suspend():使线程暂停执行,不退出可执行态。使线程暂停执行,不退出可执行态。resume()resume():将暂停的线程继续执行。将暂停的线程继续执行。setName(String s)setName(String s):赋予线程一个名字。:赋予线程一个名字。getName()getName():获得调用线程的名字。:获得调用线程的名字。getPriority()getPriority():获得调用线程的优先级。:获得调用线程的优先级。setPriority(int p)setPriority(int p):设置线程的优先级。:设置线程的优先级。join()join():等待线程死亡,若中断了该线程,:等待线程死亡,若中断了该线程,将抛出异常。将抛出异常。【实例8-1】1.1.class class getThreadInfogetThreadInfo 2.2.public static void public static void main(Stringmain(String argsargs)3.3.Thread Thread currcurr;4.4.intint num=7;num=7;5.5.currcurr=Thread.currentThreadThread.currentThread();();6.6.curr.setPriority(numcurr.setPriority(num););7.7.System.out.printlnSystem.out.println(当前线程:当前线程:+currcurr););8.8.System.out.printlnSystem.out.println(线程名:线程名:+curr.getNamecurr.getName();();9.9.System.out.printlnSystem.out.println(优先级优先级 :+curr.getPrioritycurr.getPriority(););10.10.11.11.程序输出结果:当前线程:当前线程:ThreadmainThreadmain,7 7,mainmain线程名线程名 :mainmain优先级优先级 :7 78.2.2 线程的生命周期线程的生命周期Java支持一种“抢占式”(preemptive)调度方式“Newborn”(新建)状态:线程在己被创建但未执行这段时间内,处于一个特殊线程在己被创建但未执行这段时间内,处于一个特殊的的NewbornNewborn状态,这时,线程对象己被分配内存空间,状态,这时,线程对象己被分配内存空间,其私有数据己被初始化,但该线程还未被调度。此时其私有数据己被初始化,但该线程还未被调度。此时线程对象可通过线程对象可通过startstart()方法调度,或者利用()方法调度,或者利用stopstop()()方法杀死方法杀死.新创建的线程一旦被调度,就将切换到新创建的线程一旦被调度,就将切换到RunnableRunnable状态。状态。8.2.2 线程的生命周期线程的生命周期Runnable(就绪)状态:表示线程正等待处理器资源,随时可被调用执行。处表示线程正等待处理器资源,随时可被调用执行。处于就绪状态的线程事实上己被调度,也就是说,它们于就绪状态的线程事实上己被调度,也就是说,它们己经被放到某一队列等待执行。处于就绪状态的线程己经被放到某一队列等待执行。处于就绪状态的线程何时可真正执行,取决于线程优先级以及队列的当前何时可真正执行,取决于线程优先级以及队列的当前状况。线程的优先级如果相同,将遵循状况。线程的优先级如果相同,将遵循 先来先服务先来先服务 的调度原则。的调度原则。8.2.2 线程的生命周期线程的生命周期“Running”(运行)状态:表明线程正在运行,该线己经拥有了对处理器的控制表明线程正在运行,该线己经拥有了对处理器的控制权,其代码目前正在运行。这个线程将一直运行直到权,其代码目前正在运行。这个线程将一直运行直到运行完毕,除非运行过程的控制权被一优先级更高的运行完毕,除非运行过程的控制权被一优先级更高的线程强占。线程强占。8.2.2 线程的生命周期线程的生命周期“Blocked”(堵塞)状态:一个线程如果处于一个线程如果处于BlockedBlocked(堵塞)状态,那么暂时这个线程将(堵塞)状态,那么暂时这个线程将无法进入就绪队列。处于堵塞状态的线程通常必须由某些事件才无法进入就绪队列。处于堵塞状态的线程通常必须由某些事件才能唤醒。至于是何种事件,则取决于堵塞发生的原因:处于睡眠能唤醒。至于是何种事件,则取决于堵塞发生的原因:处于睡眠中的线程必须被堵塞一段固定的时间中的线程必须被堵塞一段固定的时间;被挂起、或处于消息等待状被挂起、或处于消息等待状态的线程则必须由一外来事件唤醒。态的线程则必须由一外来事件唤醒。“Dead”(死亡)状态:DeadDead表示线程巳退出运行状态,并且不再进入就绪队列。其中原表示线程巳退出运行状态,并且不再进入就绪队列。其中原因可能是线程巳执行完毕(正常结束),也可能是该线程被另一因可能是线程巳执行完毕(正常结束),也可能是该线程被另一线程所强行中断(线程所强行中断(killkill)。)。图8-1 线程生命周期示意图start创建就绪运行挂起睡眠阻塞结束等待时间片结束分配时间片睡眠时间结束notifynotify AllsleepwaitstopI/O请求suspendresumeI/O请求结束8.2.3 创建多线程的方法创建多线程的方法Java中编程实现多线程应有两种途径一种是创建一种是创建ThreadThread线程的子类线程的子类实现一个接口实现一个接口RunnableRunnable无论是哪种途径最终都需要使用无论是哪种途径最终都需要使用ThreadThread类及其方法。类及其方法。8.2.3 创建多线程的方法创建多线程的方法1通过继承Thread类实现多线程(1 1)定义定义ThreadThread类的一个子类。类的一个子类。(2 2)定义子类中的方法定义子类中的方法run()run(),覆盖父类中的方法,覆盖父类中的方法runrun()()。(3 3)创建该子类的一个线程对象。)创建该子类的一个线程对象。(4 4)通过通过start()start()方法启动线程。方法启动线程。【实例8-2】1.1.class class myThreadmyThread extends Thread extends Thread2.2.intint sleeptimesleeptime;3.3.public public myThread(StringmyThread(String id)/id)/构造函数构造函数4.4.super(idsuper(id););5.5.sleeptimesleeptime=(=(int)(Math.randomint)(Math.random()*100);()*100);6.6.System.out.println(TheSystem.out.println(The Thread Name=+Thread Name=+getNamegetName()+()+7.7.,SleepingSleeping:+sleeptimesleeptime););8.8.9.9.public void run()public void run()10.10.try /try /通过线程睡眠模拟程序的执行通过线程睡眠模拟程序的执行 11.11.Thread.sleep(sleeptimeThread.sleep(sleeptime););12.12.13.13.catch(InterruptedExceptioncatch(InterruptedException e)e)14.14.System.err.println(ExceptionSystem.err.println(Exception:+e.toStringe.toString();();15.15.16.16.System.out.println(TheSystem.out.println(The running Thread=+running Thread=+getNamegetName();();17.17.18.18.【实例8-2】19.19.public class public class fourThreadsfourThreads 20.20.public static void public static void main(Stringmain(String argsargs)21.21.myThreadmyThreadt1t1,t2t2,t3t3,t4;t4;22.22.t1=new t1=new myThread(ThreadmyThread(Thread 1);1);23.23.t2=new t2=new myThread(ThreadmyThread(Thread 2);2);24.24.t3=new t3=new myThread(ThreadmyThread(Thread 3);3);25.25.t4=new t4=new myThread(ThreadmyThread(Thread 4);4);26.26.t1.start();t1.start();t2.start();t2.start();27.27.t3.start();t3.start();t4.start();t4.start();28.28.29.29.【实例8-2】程序某次的运行结果:程序某次的运行结果:he Thread Name=Thread 1he Thread Name=Thread 1,SleepingSleeping:6 6The Thread Name=Thread 2The Thread Name=Thread 2,SleepingSleeping:4949The Thread Name=Thread 3The Thread Name=Thread 3,SleepingSleeping:1919The Thread Name=Thread 4The Thread Name=Thread 4,SleepingSleeping:6969The running Thread=Thread 1The running Thread=Thread 1The running Thread=Thread 3The running Thread=Thread 3The running Thread=Thread 2The running Thread=Thread 2The running Thread=Thread 4The running Thread=Thread 4注意:注意:ThreadThread类中的类中的run()run()方法具有方法具有publicpublic属性,覆属性,覆盖该方法时,前面必须带上盖该方法时,前面必须带上publicpublic。8.2.3 创建多线程的方法创建多线程的方法2通过实现Runnable接口实现多线程(1 1)定义一个实现)定义一个实现RunnableRunnable接口的类。接口的类。(2 2)定义方法)定义方法run()run()。RunnableRunnable接口中有一个空的方接口中有一个空的方法法run()run(),实现它的类必须覆盖此方法。,实现它的类必须覆盖此方法。(3 3)创建该类的一个线程对象,并将该对象作参数,)创建该类的一个线程对象,并将该对象作参数,传递给传递给ThreadThread类的构造函数,从而生成类的构造函数,从而生成ThreadThread类的一类的一个对象。个对象。(4 4)通过)通过start()start()方法启动线程。方法启动线程。【实例8-3】1.1.class class myThreadmyThread implements implements RunnableRunnable 2.2.intint count=1 count=1,number;number;3.3.public public myThread(intmyThread(int num)num)4.4.number=num;number=num;5.5.System.out.printlnSystem.out.println(创建线程:创建线程:+number);+number);6.6.7.7.public void run()public void run()8.8.while(truewhile(true)9.9.System.out.printlnSystem.out.println(线程线程 +number+number+:计数:计数 +count);+count);10.10.if(+countif(+count=6)return;=6)return;11.11.12.12.13.13.14.14.public class public class runnableThreadsrunnableThreads 15.15.public static void public static void main(Stringmain(String argsargs)16.16.for(intfor(int i=0;i5;i+)new i=0;i5;i+)new Thread(newThread(new myThread(i+1).start();myThread(i+1).start();17.17.18.18.【实例8-3】程序运行某次的输出结果:创建线程:创建线程:1 1创建线程:创建线程:2 2线程线程 1 1:计数:计数 1 1线程线程 1 1:计数:计数 2 2线程线程 1 1:计数:计数 3 3线程线程 2 2:计数:计数 1 1线程线程 2 2:计数:计数 2 2线程线程 2 2:计数:计数 3 3创建线程:创建线程:3 3线程线程 3 3:计数:计数 1 1线程线程 3 3:计数:计数 2 2线程线程 3 3:计数:计数 3 3值得指出的是同一个实现了值得指出的是同一个实现了RunnableRunnable接口的对象作为参数产生的所有接口的对象作为参数产生的所有ThreadThread对象是同一对象下的线程。对象是同一对象下的线程。8.2.3 创建多线程的方法创建多线程的方法3两种方法的简单比较使用使用ThreadThread方法简单明了,符合大家的习惯,但是,它也有一个方法简单明了,符合大家的习惯,但是,它也有一个很大的缺点,那就是如果我们的类已经从一个类继承(如小程序很大的缺点,那就是如果我们的类已经从一个类继承(如小程序必须继承自必须继承自 Applet Applet 类),则无法再继承类),则无法再继承 Thread Thread 类。类。使用使用 Runnable Runnable 接口来实现多线程使得我们能够在一个类中包容接口来实现多线程使得我们能够在一个类中包容所有的代码,有利于封装,它的缺点在于,我们只能使用一套代所有的代码,有利于封装,它的缺点在于,我们只能使用一套代码,若想创建多个线程并使各个线程执行不同的代码,则仍必须码,若想创建多个线程并使各个线程执行不同的代码,则仍必须额外创建类,如果这样的话,在大多数情况下也许还不如直接用额外创建类,如果这样的话,在大多数情况下也许还不如直接用多个类分别继承多个类分别继承 Thread Thread 来得紧凑。来得紧凑。8.3 资源的协调与同步资源的协调与同步8.3.1 线程调度模型8.3.2 资源冲突8.3.3 同步方法8.3.1 线程调度模型线程调度模型当计算机中只有一个当计算机中只有一个CPUCPU时,同一时刻正在运行的线程只能有一个,时,同一时刻正在运行的线程只能有一个,当一个新的线程通过当一个新的线程通过new()new()创建并通过创建并通过start()start()方法启动后,线程只是方法启动后,线程只是进入就绪状态,是否能运行要看调度的结果。进入就绪状态,是否能运行要看调度的结果。线程调度程序挑选线程时,将选择处于就绪状态且优先级最高的线程。线程调度程序挑选线程时,将选择处于就绪状态且优先级最高的线程。优先级别主要分高、中、低三个级别,分别代表的数字是优先级别主要分高、中、低三个级别,分别代表的数字是1010、5 5、1 1。最小优先级的常量是最小优先级的常量是MIN_PRIORITYMIN_PRIORITY,普通的优先级的常量是,普通的优先级的常量是NORM_PRIORITYNORM_PRIORITY,最高的优先级的常量是,最高的优先级的常量是MAX_PRIORITYMAX_PRIORITY。一般主线程。一般主线程的优先级是普通。另外可以通过的优先级是普通。另外可以通过ThreadThread类的类的setPrioritysetPriority(int aint a)方)方法来修改系统自动设置的线程优先级。法来修改系统自动设置的线程优先级。如果多个线程具有相同的优先级,它们将被轮流调度。如果多个线程具有相同的优先级,它们将被轮流调度。图8-1 线程优先级调度示意图新建线程y就绪线程O的等锁池优先级多列正在运行线程xy.start()执行到synchronized(O)标记不存在则进入O等锁池O标记返还了线程可运行按优先级排队高优先级占先优先级【实例8-5】验证了Java对多线程的调度方法。1.1.class class myThreadmyThread extends Thread extends Thread 2.2.myThread(StringmyThread(String strstr)super(strsuper(str););3.3.public void run()public void run()4.4.trytryThread.sleep(2);Thread.sleep(2);5.5.catch(InterruptedExceptioncatch(InterruptedException e)e)6.6.System.err.println(e.toStringSystem.err.println(e.toString();();7.7.8.8.System.out.println(theSystem.out.println(the Thread Name=+Thread Name=+getNamegetName()+)+,Priority=+Priority=+9.9.getPrioritygetPriority();();10.10.11.11.【实例8-5】验证了Java对多线程的调度方法。12.12.public class public class testTheadPrioritytestTheadPriority 13.13.public static void public static void main(Stringmain(String agrsagrs)14.14.Thread Thread minThreadminThread=new=new myThread(MinPriorityThreadmyThread(MinPriorityThread););15.15.Thread Thread normThreadnormThread=new=new myThread(NormPriorityThreadmyThread(NormPriorityThread););16.16.Thread Thread maxThreadmaxThread=new=new myThread(MaxPriorityThreadmyThread(MaxPriorityThread););17.17.minThread.setPriority(Thread.MIN_PRIORITYminThread.setPriority(Thread.MIN_PRIORITY););18.18.normThread.setPriority(Thread.NORM_PRIORITYnormThread.setPriority(Thread.NORM_PRIORITY););19.19.maxThread.setPriority(Thread.MAX_PRIORITYmaxThread.setPriority(Thread.MAX_PRIORITY););20.20.minThread.startminThread.start();();21.21.normThread.startnormThread.start();();22.22.maxThread.startmaxThread.start();();23.23.24.24.【实例8-5】程序输出结果:the Thread Name=MaxPriorityThreadthe Thread Name=MaxPriorityThread,Priority=10Priority=10the Thread Name=NormPriorityThreadthe Thread Name=NormPriorityThread,Priority=5Priority=5the Thread Name=MinPriorityThreadthe Thread Name=MinPriorityThread,Priority=1Priority=18.3.2 资源冲突资源冲突多个线程同时运行虽然可以提高程序的执行效率,但由于共享一组资源,可能会产生冲突,例如【实例8-6】。【实例8-6】1.1.class class myThreadmyThread 2.2.void void Test(intTest(int n)n)3.3.System.out.printlnSystem.out.println(运行线程运行线程 NONO:+n);+n);4.4.tryThread.sleep(3);tryThread.sleep(3);5.5.catch(InterruptedExceptioncatch(InterruptedException e)e)6.6.System.out.printlnSystem.out.println(线程异常线程异常,NONO:+n);+n);7.7.System.out.printlnSystem.out.println(结束线程结束线程 NONO:+n);+n);8.8.9.9.【实例8-6】10.10.class class rThreadrThread implements implements RunnableRunnable 11.11.myThreadmyThread ObjObj;12.12.intint num;num;13.13.rThread(myThreadrThread(myThread t t,intint n)n)14.14.ObjObj=t;=t;15.15.num=n;num=n;16.16.17.17.public void run()public void run()18.18.Obj.Test(numObj.Test(num););19.19.20.20.【实例8-6】21.21.public class public class ThreadClashThreadClash 22.22.public static void public static void main(Stringmain(String argsargs)23.23.myThreadmyThread ObjObj=new=new myThreadmyThread();();24.24.Thread t1=new Thread t1=new Thread(newThread(new rThread(Obj rThread(Obj,1);1);25.25.Thread t2=new Thread t2=new Thread(newThread(new rThread(Obj rThread(Obj,2);2);26.26.Thread t3=new Thread t3=new Thread(newThread(new rThread(Obj rThread(Obj,3);3);27.27.t1.start();t2.start();t3.start();t1.start();t2.start();t3.start();28.28.29.29.【实例8-6】程序运行结果:运行线程运行线程 NO NO:1 1运行线程运行线程 NO NO:2 2运行线程运行线程 NO NO:3 3结束线程结束线程 NO NO:1 1结束线程结束线程 NO NO:2 2结束线程结束线程 NO NO:3 38.3.3 同步方法同步方法同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题。可以通过 private 关键字来保证数据对象只能被方法访问synchronized 关键字:锁定冲突的方法和锁定冲突的对象。8.3.3 同步方法同步方法1锁标志每个对象都有一个标志锁。当对象的一个线程访问了对象的某个每个对象都有一个标志锁。当对象的一个线程访问了对象的某个synchronizedsynchronized数据时,这个对象就将被数据时,这个对象就将被“上锁上锁”被声明为被声明为synchronizedsynchronized的数据都不能被调用的数据都不能被调用只有当前线程访问完它要访问的只有当前线程访问完它要访问的synchronizedsynchronized数据者抛出了没有数据者抛出了没有在在 synchronized synchronized 块中捕获的异常时,释放块中捕获的异常时,释放“锁标志锁标志”后,同一后,同一个对象的其它线程才能访问个对象的其它线程才能访问synchronizedsynchronized数据。数据。这样,每次只有一个线程可以执行给定监控器保护的代码块。从这样,每次只有一个线程可以执行给定监控器保护的代码块。从其它线程的角度看,该代码块可以看作是原子的,它要么全部执其它线程的角度看,该代码块可以看作是原子的,它要么全部执行,要么根本不执行。行,要么根本不执行。8.3.3 同步方法同步方法1锁标志锁用于保护代码块或整个方法,必须记住是锁的身份保护了代码锁用于保护代码块或整个方法,必须记住是锁的身份保护了代码块,而不是代码块本身,这一点很重要。一个锁可以保护许多代块,而不是代码块本身,这一点很重要。一个锁可以保护许多代码块或方法。码块或方法。反之,仅仅因为代码块由锁保护并不表示两个线程不能同时执行反之,仅仅因为代码块由锁保护并不表示两个线程不能同时执行该代码块。它只表示如果两个线程正在等待相同的锁,则它们不该代码块。它只表示如果两个线程正在等待相同的锁,则它们不能同时执行该代码。能同时执行该代码。此外此外 non-static non-static的的synchronizedsynchronized数据只能在同一个对象的纯种实数据只能在同一个对象的纯种实现同步访问,不同对象的线程仍可同时访问。现同步访问,不同对象的线程仍可同时访问。每个每个classclass也有一个也有一个“锁标志锁标志”。对于。对于synchronized staticsynchronized static数据数据可以在整个可以在整个classclass下进行锁定,避免下进行锁定,避免staticstatic数据的同时访问。对象数据的同时访问。对象的的“锁标志锁标志”和和classclass的的“锁标志锁标志”是相互独立的。这点在前面已是相互独立的。这点在前面已举例说明,在此不在赘述。举例说明,在此不在赘述。8.3.3 同步方法同步方法2.锁定冲突的方法即在待锁定方法声明中加入即在待锁定方法声明中加入 synchronized synchronized关键字。这种加上关键字。这种加上synchronizedsynchronized关键字的方法也称关键字的方法也称synchronizedsynchronized方法。方法。格式为:格式为:public synchronized void accessVal(int newVal);public synchronized void accessVal(int newVal);synchronizedsynchronized方法控制对类成员变量的访问:每个类实例对应一把锁,方法控制对类成员变量的访问:每个类实例对应一把锁,每个每个synchronized synchronized 方法都必须获得调用该方法的类实例的锁方能执行,方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronizedsynchronized)。)。8.3.3 同步方法同步方法2.锁定冲突的方法synchronized synchronized 方法的缺陷:若将一个大的方法声明为方法的缺陷:若将一个大的方法声明为synchronized synchronized 将会大大影响效率,典型地,若将线程将会大大影响效率,典型地,若将线程类的方法类的方法 run()run()声明为声明为 synchronized synchronized,由于在线程,由于在线程的整个生命期内它一直在运行,因此将导致它对本类的整个生命期内它一直在运行,因此将导致它对本类任何任何 synchronized synchronized 方法的调用都永远不会成功。当方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为方法中,将其声明为 synchronized synchronized,并在主方法中,并在主方法中调用来解决这一问题,但是调用来解决这一问题,但是 Java Java 为我们提供了更好为我们提供了更好的解决办法,那就是的解决办法,那就是 synchronized synchronized 块。块。【实例8-7】1.1.public class SyncThreads1public class SyncThreads12.2.private static private static intint x x,y;y;3.3.private static class Thread1 extends Thread private static class Thread1 extends Thread 4.4.public synchronized void run()public synchronized void run()5.5.x=y=0;x=y=0;6.6.System.out.println(xSystem.out.println(x););7.7.8.8.9.9.private static class Thread2 extends Thread private static class Thread2 exten
展开阅读全文