您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 商业计划书 > 关于java语言实现并发编程的研究综述
1关于java语言实现并发编程的研究综述【摘要】JAVA一个重要的特点就是支持多线程编程,多线程编程是实现并发编程的一种方式。本文简述了与并发编程相关的一些概念,并通过实例阐述了如何利用多线程进行并发编程,介绍了JAVA多线程的同步机制以及死锁等问题。【关键词】JAVA;多线程;并发编程一、背景随着多核处理器的普及,以及人们对高性能计算需求的不断扩大和各种新技术的出现,并发编程模型也处于不断的发展和完善之中。传统编程环境通常是单线程的,而Java是支持多线程的。由于一个CPU在同一时刻只能执行一个程序中的一条指令,所以在单核处理器环境下,人们所看到的程序能够并行的执行,实际上是进程被交替执行,表现出一种并发的外部特种,是伪并行;而在多核处理器的环境下,才是真并行,进程不仅可以交替执行,而且可以重叠执行。并行编程是在多核处理器的情况下才会出现的,本文就着重讨论下多核处理器下的并发/多线程编程。二、并发等相关概念概述1、并发与并行并发是指两个或多个程序在同一时间间隔内发生,如果在单核处理器上,看似程序同时执行,实际上是交替执行;如果在多核处理器上,我们看到的也是同时执行,实际上又分为两种情况:一种是程序在不同的处理器上在同一时刻同时执行,我们把这种情况称为并行;另一种情况就是交替执行。所以只有在多核处理器的情况下才有可能实现并行,并行具有并发的含义,而并发不一定是并行。2、同步与异步同步是指发送一个请求等待返回,然后再发送下一个请求;异步是指发送一个请求不等待返回,随时可以再发送下一个请求。异步和同步是相对的,同步就是顺序执行,执行完一个再执行下一个,需要等待、协调运行。异步就是彼此独立,在等待某事件的过程中继续做自己的事,不需要等待这个事件完成后再工作。3、进程与多线程进程是指程序的一次执行过程,或是正在运行的一个程序,它具有一个独立的执行环境。2线程有时也被称为轻量级的进程,线程是进程的进一步细化,是一个程序内部的一条执行路径,若一个程序可同一时间执行多个线程,就是支持多线程的。4、异步与多线程异步和多线程并不是一个同等关系,异步是最终目的,多线程只是我们实现异步的一个方式。异步是让调用方法的主线程不需要同步等待另一线程的完成,从而可以让主线程干其它的事情。三、java多线程编程的优缺点我们知道Java语言具有简单性、纯面向对象、可移植性、健壮性、安全性、分布性等特点,同时Java语言也提供了对多线程的良好支持,使得资源利用率更好,程序设计在某些情况下也更简单,程序响应更快等,正是因为有这些优点,多线程一直发展至今。事情总有两面性,从一个单线程的应用到一个多线程的应用并不仅仅带来好处,也会有一些代价。多线程程序设计更复杂,虽然有时候一些多线程的程序比单线程的程序要简单一些,但其他方面都更为复杂,比如多线程访问共享数据时,如不进行同步处理,就会容易造成访问数据错误,对于这样的错误又很难发现,并且问题难以重现及修复;还有等待临界资源时,也会使速度下降;线程在运行时不仅占用cpu,同时也需要一些内存来维持它本地的堆栈,还要占用操作系统中的一些资源,这增加了资源的消耗;对线程的管理也会增加CPU的额外负担;由于缺乏成效,但又很复杂将会降低效率;还有线程的饥饿、竞争和死锁等问题,这些都有待于继续研究。四、利用Java多线程机制实现并发编程1、多线程编程形式创建线程需要一定的步骤,首先要创建线程,然后为其指定工作,当工作结整后再毙掉该线程。通常在Java中线程的编程形式有两种:(1)继承Thread类1)定义子类继承Thread类;2)子类中重写Thread类中的run方法;3)创建Thread子类对象,即创建了线程对象;4)调用线程对象start方法:启动线程,调用run方法。如下代码:实现两个线程打印10以内的自然数3图1图2是程序执行结果,因为线程是交替执行,所以执行的结果也会不同图1图2(2)实现Runnable接口1)定义子类,实现Runnable接口;2)子类中重写Runnable接口中的run方法;3)通过Thread类含参构造器创建线程对象;4)将Runnable接口的子类对象作为实际参数传递给Thread类的构造方法中;5)调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。如下代码:用接口的形式实现两个线程打印10以内的自然数classPrintNumextendsThread{publicvoidrun(){for(inti=1;i=10;i++){System.out.println(继承Thread类---+Thread.currentThread().getName()+:+i);}}}publicclasstest{publicstaticvoidmain(String[]args){PrintNump1=newPrintNum();PrintNump2=newPrintNum();p1.start();p2.start();}}4图3图4是程序执行结果,用实现接口的形式跟继承都可以达到同样的目的图3图42、设置线程的优先级如上图执行结果所示,当有多个线程运行时优先抢占到CPU的线程得以优先执行,实际中可能每个线程的重要程度不尽相同,需要让某些线程优先执行,这就涉及到线程的优先级了,优先级高的线程得到CPU的时间长一些,但也不是说优先级高的全部执行完才执行优先级低的,而是优先级高的获取执行权的概率要高一些。优先级的设置要注意几点:(1)确定优先级必须采用1~10之间的整数;classPrintNum1implementsRunnable{publicvoidrun(){for(inti=1;i=10;i++){System.out.println(实现Runnable接口---+Thread.currentThread().getName()+:+i);}}}publicclasstest{publicstaticvoidmain(String[]args){PrintNum1p=newPrintNum1();Threadt1=newThread(p);Threadt2=newThread(p);t1.start();t2.start();}}5(2)父线程的优先级要被子线程所继承;(3)线程的优先级可以通过setpriority()的调用进行改变。在第一个程序代码中加入如下两行代码,分别设置p1的优先级为1,p2的优先级为10,则p2可能就会优先获取CPU得以执行。p1.setPriority(Thread.MIN_PRIORITY);//1p2.setPriority(Thread.MAX_PRIORITY);//10图5为设置优先级后的执行结果:图53、多线程安全问题在多线程程序中,由于同时有多个线程并发执行,会造成访问冲突,也就是说当一个线程在操作共享数据时,还没有执行完毕的情况下,另外的线程就参与进来了,导致共享数据存在安全问题,下面以火车站售票为例,三个窗口卖票相当于起了三个线程,我们看一下打印车票的情况:由于数据少的情况下比较难以显示出问题,这里用了sleep()方法,使打印重复票的问题更容易重现一些。6图6图7classWindowextendsThread{staticintticket=50;publicvoidrun(){while(true){if(ticket0){try{Thread.currentThread().sleep(10);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+售票,票号为:+ticket--);}else{break;}}}}publicclasstest{publicstaticvoidmain(String[]args){Windoww1=newWindow();Windoww2=newWindow();Windoww3=newWindow();w1.setName(窗口1);w2.setName(窗口2);w3.setName(窗口3);w1.start();w2.start();w3.start();}}7如图6执行结果,出现了窗口2和窗口3都打印了票号为44的,这是现实中所不容许的,这就是多线程访问共享资源引起的数据安全问题,怎样解决安全问题呢?java提供了synchronize,对不安全的部分加锁。4、同步机制如果程序是单线程的,我们就不用考虑数据安全问题了,但像上述“火车站售票”,就有数据安全问题了,Java语言提供synchronized关键字,为防止资源冲突提供了两种方式(1)synchronized块synchronized(对象){//需要被同步的代码;}(2)synchronized方法synchronized还可以放在方法声明中,表示整个方法为同步方法。例如:publicsynchronizedvoidshow(Stringname){//需要被同步的代码;}将上述“火车站售票”的代码,run方法中的共享数据用关键字synchronized修改如下:如图7执行结果,这样就解决了打印车票时出现重票、错票的问题。5、线程阻塞和死锁在多线程运行的时候,可能一个线程运行的条件是另一个线程运行的结果,这就要求我们让当前线程等待另一个线程,这就是所谓的让线程阻塞。Java提供了对阻塞机制的支持,如上述所用到的sleep()方法,还有resume()、suspend()、yield()、notify()、wait()等。线程死锁有个很经典的案例哲学家问题,有两根筷子,两个哲学家,只有当两根筷子在一起时才能吃饭,结果有一种情况是两个哲学家各拿到一根筷子,没有人放手,然后等待等待,最后都饿死了。这就相当于两个线程访问两个共享资源,互不相让,造成了线程死锁。下面看一个线程死锁的例子:当第一个线程获取cpu执行权时把sb1锁住了,与此同时可能第二个线程也把sb2锁住了,synchronized(this){if(ticket0){try{Thread.currentThread().sleep(10);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+售票,票号为:+ticket--);}}8第一个线程要访问sb2,第二个线程要访问sb1,一直等待下去,就出现了死锁。publicclasstest{staticStringBuffersb1=newStringBuffer();staticStringBuffersb2=newStringBuffer();publicstaticvoidmain(String[]args){newThread(){publicvoidrun(){synchronized(sb1){try{Thread.currentThread().sleep(10);}catch(InterruptedExceptione){e.printStackTrace();}sb1.append(A);synchronized(sb2){sb2.append(B);System.out.println(sb1);System.out.println(sb2);}}}}.start();newThread(){publicvoidrun(){synchronized(sb2){try{Thread.currentThread().sleep(10);}catch(InterruptedExceptione){e.printStackTrace();}sb1.append(C);synchronized(sb1){sb2.append(D);System.out.prin
本文标题:关于java语言实现并发编程的研究综述
链接地址:https://www.777doc.com/doc-2665534 .html