java网络编程第2章.ppt
第二章,线程管理,课程目标,线程的优先级:优先级概述线程优先级的具体应用线程同步:线程同步的目的线程同步的具体应用线程死锁:线程死锁产生的直接原因产生死锁的必要条件与解决方法wait和notify机制,体验项目,复盘就是每次博弈结束以后,双方棋手把刚才的对局再重复一遍,现在以复盘过程中的两个线程为例,在运行过程中,其中有一个线程用于下黑棋、另外一个线程用于下白棋,还有一个用于存放下棋步骤的Vector。,线程优先级,优先级概述 上一章我们只介绍了优先级的概念,下面我们对优先级作具体的介绍。多线程运行时需要定义线程运行的先后顺序,而先后顺序的设置在JAVA中我们通过设计线程优先级来实现,线程优先级是用数字表示,数字越大线程优先级越高,但是最大值不可超过10,线程优先级的数值位于数值Thread.MIN_PRIORITY和数值Thread.MAX_PRIORITY之间(分别是1和10)。默认优先级Thread.NORM_PRIORITY的数值是这两者中间的数(为5)。新线程的优先级和产生它们的线程优先级是一样的。,关于线程优先级的设置,可用setPriority(int level)方法来设置线程的优先级,也可以用getPriority()方法获得线程的优先级。如果一个线程当前正在执行,可以用setPriority(int)把线程的优先级设置得比以前更低,优先级设置太低线程则有可能停止执行,因为现在可能有其他更高优先级的线程要执行。所以,可以通过优先级确保程序像期望的那样对用户的请求做出响应。用户不必依靠JAVA虚拟机在具有相同优先级的线程上切换。我们可以通过调用yield()方法,让当前执行的线程放弃控制权。如果这样的话,JAVA会挑选一个新的线程来运行,但是如果刚刚放弃的是具有最高优先级的线程,它也有可能又立即运行。,优先级的具体应用,线程优先级的设置 在有多个线程运行时可以通过设置优先级来定义线程的执行顺序,通过线程的setPriority(int level)方法来设置优先级。public class PriThreadpublic static void main(String args)ThreadA a=new ThreadA();ThreadB b=new ThreadB();a.setPriority(2);/设置优先级别,数值越大优先级越高b.setPriority(3);a.start();b.start();,class ThreadA extends Threadpublic void run()System.out.println(我是线程A);class ThreadB extends Threadpublic void run()System.out.println(我是线程B);因为在代码段当中我们把线程B的优先级设置高于线程A,所以运行结果先执行线程B的run()方法后再执行线程A的run()方法。,线程优先级的获得,JAVA中获得线程优先级的方法,是通过getPriority()方法来实现的。public class PriThreadpublic static void main(String args)Thread a=new Thread();Thread b=new Thread();int priA=a.getPriority();/获得优先级的方法int priB=b.getPriority();System.out.println(priA);System.out.println(priB);,设置优先级也可以用线程常量MAX_PRIORITY、MIN_PRIORITY、NORM_PRIORITY来设置,其中MAX_PRIORITY为最高优先级;MIN_PRIORITY为最低优先级;NORM_PRIORITY是系统分配给线程的默认优先级。下面我们通过示例来说明它们的用法。,public class PriConstantpublic static void main(String args)Thread a=new Thread();int temp=Thread.MAX_PRIORITY;a.setPriority(temp);/设置此线程优先级最高System.out.println(a.getPriority();temp=Thread.MIN_PRIORITY;a.setPriority(temp);/设置此线程优先级最低System.out.println(a.getPriority();temp=Thread.NORM_PRIORITY;a.setPriority(temp);/将线程优先级设置为默认 System.out.println(a.getPriority();,线程同步,线程同步的目的 一个程序中有多个线程在执行时,由于线程的速度无法预知,所以对于共用数据的插入、删除、更新等动作,若不做适当协调的话,所取得的数据有可能不正确。可能甲线程正在更新,但未完成,而乙线程已经将数据取走了,这样的话数据当然不正确。为了避免这种情况,JAVA提供了一种同步机制的办法,在JAVA里同步是用synchronized关键字来实现的,synchronized关键字的作用是,确保在某个时刻只有一个线程允许执行特定的代码块,它有两种写法:一种是同步方法,另一种是同步对象,具体应用将在后面小节里介绍。,同步的具体应用,多线程将异步行为引入程序,所以在需要同步时,必须有一种方法强制进行同步,这样可以保证读写共享数据时的正确性,常用的方法是阻止这两个线程同时访问同一变量。在JAVA中同步很简单,所有对象都有和自己关联的隐含监控器,如果要进入一个对象的监控器,只需调用有synchronized关键词修饰的方法即可。当一个线程进入一个同步的方法时,所有其他试图调用同一实例中的该方法的线程必须等待。synchronized关键字确保了对某些一次只能有一个线程可以执行的代码进行保护(互斥),而且它确保了一个线程更改后的数据对于其它线程是可见的(更改的可见性),synchronized关键词有两种使用方法:分别是同步方法和同步对象。(1)同步方法就是在方法前加上synchronized关键字,就实现了同步,下面是使用同步方法的示例,Target类的show()方法实现了同步,线程ThreadA和线程ThreadB同时得到了Target对象,并且都想执行show()方法,代码如下。,public class TestSyncpublic static void main(String args)Target t=new Target();new ThreadA(t).start();new ThreadB(t).start();class TargetString msg=;public synchronized void show()/同步方法char msgchars=msg.toCharArray();for(int i=0;imsgchars.length;i+)System.out.print(msgcharsi);tryThread.sleep(1000);catch(Exception e),class ThreadA extends Thread/线程ATarget target;public ThreadA(Target target)this.target=target;public void run()target.show();class ThreadB extends Thread/线程BTarget target;public ThreadB(Target target)this.target=target;public void run()target.show();,注意:以下运行结果,因为两个线程都使用了同一个Target对象,Target对象的show()方法又声明为同步,这样会让两个线程依次使用他们的show()方法,所以在这里两个线程分别运行没有交叉打印,即:一个线程调用完show()方法之后,另一个线程才可以调用。,(2)同步的另一种写法就是同步对象,而同步对象名副其实就是对对象进行了同步 同步对象的写法如下:synchronized(target)target.show()其中Target为需要同步的对象,例2.4的同步方法只是对Target类的show()方法进行了同步,而同步对象则是把Target对象进行了同步,即:对Target对象里的所有方法全部都进行同步。下面是一个同步对象的示例:,public class TestSyncpublic static void main(String args)Target t=new Target();new ThreadA(t).start();new ThreadB(t).start();class TargetString msg=;public void show()char msgchars=msg.toCharArray();for(int i=0;imsgchars.length;i+)System.out.print(msgcharsi);tryThread.sleep(1000);catch(Exception e),class ThreadA extends ThreadTarget target;public ThreadA(Target target)this.target=target;public void run()synchronized(target)/同步对象target.show();class ThreadB extends ThreadTarget target;public ThreadB(Target target)this.target=target;public void run()synchronized(target)/同步对象target.show();,线程死锁,线程死锁产生的直接原因 线程死锁产生的直接原因是占用共享资源的线程正在请求其他共享资源,而该资源正在被其他线程占用,且占用该资源的线程还在请求其他共享资源,从而形成了线程永远无法得到继续执行所需要的资源,进而导致线程无法继续运行。,产生死锁的必要条件与解决方法,互斥条件:一个资源(临界资源)每次只能为一个线程使用,而且使用期间是互斥的。不可抢占的条件:一个线程已经占有资源,未经本线程释放的情况下,其它线程不能强行剥夺。占有且申请条件:(部分分配条件)线程投入时,不是一次性申请所需资源,而是运行中按需要临时动态地申请。循环等待条件:系统中几个线程形成循环地等待对方所占用资源的关系。,为了找到死锁的解决方法,我们先看下面的一个死锁图,P1、P2表示两个线程,R1、R2表示资源,P1已经占用资源R1而且在等待R2,P2已经占用资源R2而且在等待R1,这时就会产生两个线程互相等待的状态。从上图可以看出,只要去掉其中的任何一条线,就不会形成死锁。实际上,预防死锁有一个基本思想:要求线程申请资源时遵循某种协议,从而打破产生死锁的四个条件中的一个或几个,从而保证系统不会进入死锁状态。,wait和notify机制,在实际应用中,多线程之间常常需要互相协调工作。例如,浏览器的一个显示图片的线程displayThread想要执行显示图片的任务,必须等待下载线程downloadThread将该图片下载完毕。如果图片还没有下载完,线程displayThread可以暂停,当线程downloadThread完成了任务后,再通知线程displayThread并显示“图片准备完毕,可以显示了”,这时,displayThread继续执行。以上逻辑简单的说就是:如果条件不满足,则等待。当条件满足时,等待该条件的线程将被唤醒。在JAVA中,这个机制的实现依赖于wait()/notify()方法。等待机制与唤醒机制是密切关联的。synchronized(obj)while(!condition)obj.wait();obj.doSomething();,当线程A获得了obj锁后,发现条件condition不满足,无法继续下一步处理,于是线程A就wait()。在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A,如下所示:synchronized(obj)condition=true;obj.notify();,实践项目,实践项目的具体要求如下:以在线下棋步骤(复盘)的两个线程为例,在运行过程中,其中有一个线程用于下黑棋、另外一个线程用于下白棋,还有一个用于存放下棋步骤的Vector,他的显示效果为:运行时打开一个窗体,上面加载了一个棋盘,之后黑棋白棋先后交替下棋,间隔为一秒钟,直到复盘结束为止。,本章总结,C语言概述与发展C语言的特点程序设计语言概述C语言程序结构C语言程序书写时的注意事项C语言程序格式上的其他特点C语言开发环境-Visual C+6.0的使用,