首页 > 精品范文库 > 14号文库
Java多线程编程总结(★)
编辑:烟雨迷离 识别码:23-283631 14号文库 发布时间: 2023-04-02 01:39:18 来源:网络

第一篇:Java多线程编程总结

Java多线程编程总结

2007-05-17 11:21:59 标签:java 多线程

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处、作者信息和本声明。否则将追究法律责任。http://lavasoft.blog.51cto.com/62575/27069

Java多线程编程总结

下面是Java线程系列博文的一个编目:

Java线程:概念与原理 Java线程:创建与启动

Java线程:线程栈模型与线程的变量 Java线程:线程状态的转换 Java线程:线程的同步与锁 Java线程:线程的交互 Java线程:线程的调度-休眠 Java线程:线程的调度-优先级 Java线程:线程的调度-让步 Java线程:线程的调度-合并 Java线程:线程的调度-守护线程 Java线程:线程的同步-同步方法 Java线程:线程的同步-同步块

Java线程:并发协作-生产者消费者模型 Java线程:并发协作-死锁 Java线程:volatile关键字 Java线程:新特征-线程池

Java线程:新特征-有返回值的线程 Java线程:新特征-锁(上)Java线程:新特征-锁(下)Java线程:新特征-信号量 Java线程:新特征-阻塞队列 Java线程:新特征-阻塞栈 Java线程:新特征-条件变量 Java线程:新特征-原子量 Java线程:新特征-障碍器 Java线程:大总结

----

下面的内容是很早之前写的,内容不够充实,而且是基于Java1.4的内容,Java5之后,线程并发部分扩展了相当多的内容,因此建议大家看上面的系列文章的内容,与时俱进,跟上Java发展的步伐。----

一、认识多任务、多进程、单线程、多线程 要认识多线程就要从操作系统的原理说起。

以前古老的DOS操作系统(V 6.22)是单任务的,还没有线程的概念,系统在每次只能做一件事情。比如你在copy东西的时候不能rename文件名。为了提高系统的利用效率,采用批处理来批量执行任务。

现在的操作系统都是多任务操作系统,每个运行的任务就是操作系统所做的一件事情,比如你在听歌的同时还在用MSN和好友聊天。听歌和聊天就是两个任务,这个两个任务是“同时”进行的。一个任务一般对应一个进程,也可能包含好几个进程。比如运行的MSN就对应一个MSN的进程,如果你用的是windows系统,你就可以在任务管理器中看到操作系统正在运行的进程信息。

一般来说,当运行一个应用程序的时候,就启动了一个进程,当然有些会启动多个进程。启动进程的时候,操作系统会为进程分配资源,其中最主要的资源是内存空间,因为程序是在内存中运行的。在进程中,有些程序流程块是可以乱序执行的,并且这个代码块可以同时被多次执行。实际上,这样的代码块就是线程体。线程是进程中乱序执行的代码流程。当多个线程同时运行的时候,这样的执行模式成为并发执行。

多线程的目的是为了最大限度的利用CPU资源。

Java编写程序都运行在在Java虚拟机(JVM)中,在JVM的内部,程序的多任务是通过线程来实现的。每用java命令启动一个java应用程序,就会启动一个JVM进程。在同一个JVM进程中,有且只有一个进程,就是它自己。在这个JVM环境中,所有程序代码的运行都是以线程来运行。

一般常见的Java应用程序都是单线程的。比如,用java命令运行一个最简单的HelloWorld的Java应用程序时,就启动了一个JVM进程,JVM找到程序程序的入口点main(),然后运行main()方法,这样就产生了一个线程,这个线程称之为主线程。当main方法结束后,主线程运行完成。JVM进程也随即退出。

对于一个进程中的多个线程来说,多个线程共享进程的内存块,当有新的线程产生的时候,操作系统不分配新的内存,而是让新线程共享原有的进程块的内存。因此,线程间的通信很容易,速度也很快。不同的进程因为处于不同的内存块,因此进程之间的通信相对困难。

实际上,操作的系统的多进程实现了多任务并发执行,程序的多线程实现了进程的并发执行。多任务、多进程、多线程的前提都是要求操作系统提供多任务、多进程、多线程的支持。

在Java程序中,JVM负责线程的调度。线程调度是值按照特定的机制为多个线程分配CPU的使用权。调度的模式有两种:分时调度和抢占式调度。分时调度是所有线程轮流获得CPU使用权,并平均分配每个线程占用CPU的时间;抢占式调度是根据线程的优先级别来获取CPU的使用权。JVM的线程调度模式采用了抢占式模式。

所谓的“并发执行”、“同时”其实都不是真正意义上的“同时”。众所周知,CPU都有个时钟频率,表示每秒中能执行cpu指令的次数。在每个时钟周期内,CPU实际上只能去执行一条(也有可能多条)指令。操作系统将进程线程进行管理,轮流(没有固定的顺序)分配每个进程很短的一段是时间(不一定是均分),然后在每个线程内部,程序代码自己处理该进程内部线程的时间分配,多个线程之间相互的切换去执行,这个切换时间也是非常短的。因此多任务、多进程、多线程都是操作系统给人的一种宏观感受,从微观角度看,程序的运行是异步执行的。

用一句话做总结:虽然操作系统是多线程的,但CPU每一时刻只能做一件事,和人的大脑是一样的,呵呵。

二、Java与多线程

Java语言的多线程需要操作系统的支持。

Java 虚拟机允许应用程序并发地运行多个执行线程。Java语言提供了多线程编程的扩展点,并给出了功能强大的线程控制API。

在Java中,多线程的实现有两种方式: 扩展java.lang.Thread类 实现java.lang.Runnable接口

每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。每个线程都可以或不可以标记为一个守护程序。当某个线程中运行的代码创建一个新 Thread 对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程才是守护程序。

当 Java 虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的 main 方法)。Java 虚拟机会继续执行线程,直到下列任一情况出现时为止:

调用了 Runtime 类的 exit 方法,并且安全管理器允许退出操作发生。

非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,还是通过抛出一个传播到 run 方法之外的异常。

三、扩展java.lang.Thread类

/** * File Name: TestMitiThread.java * Created by: IntelliJ IDEA.* Copyright: Copyright(c)2003-2006 * Company: Lavasoft([url]http://lavasoft.blog.51cto.com/[/url])* Author: leizhimin * Modifier: leizhimin * Date Time: 2007-5-17 10:03:12 * Readme: 通过扩展Thread类实现多线程 */ public class TestMitiThread { public static void main(String[] rags){ System.out.println(Thread.currentThread().getName()+ “ 线程运行开始!”);new MitiSay(“A”).start();new MitiSay(“B”).start();System.out.println(Thread.currentThread().getName()+ “ 线程运行结束!”);} }

class MitiSay extends Thread { public MitiSay(String threadName){ super(threadName);}

public void run(){ System.out.println(getName()+ “ 线程运行开始!”);for(int i = 0;i < 10;i++){ System.out.println(i + “ ” + getName());try { sleep((int)Math.random()* 10);} catch(InterruptedException e){ e.printStackTrace();} } System.out.println(getName()+ “ 线程运行结束!”);} }

运行结果:

main 线程运行开始!main 线程运行结束!A 线程运行开始!0 A 1 A B 线程运行开始!2 A 0 B 3 A 4 A 1 B 5 A 6 A 7 A 8 A 9 A A 线程运行结束!2 B 3 B 4 B 5 B 6 B 7 B 8 B 9 B B 线程运行结束!说明:

程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()调用时候被创建。随着调用MitiSay的两个对象的start方法,另外两个线程也启动了,这样,整个应用就在多线程下运行。

在一个方法中调用Thread.currentThread().getName()方法,可以获取当前线程的名字。在mian方法中调用该方法,获取的是主线程的名字。

注意:start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable),什么时候运行是由操作系统决定的。

从程序运行的结果可以发现,多线程程序是乱序执行。因此,只有乱序执行的代码才有必要设计为多线程。

Thread.sleep()方法调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留出一定时间给其他线程执行的机会。

实际上所有的多线程代码执行顺序都是不确定的,每次执行的结果都是随机的。

四、实现java.lang.Runnable接口

/** * 通过实现 Runnable 接口实现多线程 */ public class TestMitiThread1 implements Runnable {

public static void main(String[] args){ System.out.println(Thread.currentThread().getName()+ “ 线程运行开始!”);TestMitiThread1 test = new TestMitiThread1();Thread thread1 = new Thread(test);Thread thread2 = new Thread(test);thread1.start();thread2.start();System.out.println(Thread.currentThread().getName()+ “ 线程运行结束!”);}

public void run(){ System.out.println(Thread.currentThread().getName()+ “ 线程运行开始!”);for(int i = 0;i < 10;i++){ System.out.println(i + “ ” + Thread.currentThread().getName());try { Thread.sleep((int)Math.random()* 10);} catch(InterruptedException e){ e.printStackTrace();} } System.out.println(Thread.currentThread().getName()+ “ 线程运行结束!”);} }

运行结果:

main 线程运行开始!Thread-0 线程运行开始!main 线程运行结束!0 Thread-0 Thread-1 线程运行开始!0 Thread-1 1 Thread-1 1 Thread-0 2 Thread-0 2 Thread-1 3 Thread-0 3 Thread-1 4 Thread-0 4 Thread-1 5 Thread-0 6 Thread-0 5 Thread-1 7 Thread-0 8 Thread-0 6 Thread-1 9 Thread-0 7 Thread-1 Thread-0 线程运行结束!8 Thread-1 9 Thread-1 Thread-1 线程运行结束!说明:

TestMitiThread1类通过实现Runnable接口,使得该类有了多线程类的特征。run()方法是多线程程序的一个约定。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。

在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target)构造出对象,然后调用Thread对象的start()方法来运行多线程代码。

实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。

五、读解Thread类API

static int MAX_PRIORITY 线程可以具有的最高优先级。static int MIN_PRIORITY 线程可以具有的最低优先级。static int NORM_PRIORITY 分配给线程的默认优先级。

构造方法摘要

Thread(Runnable target)分配新的 Thread 对象。Thread(String name)分配新的 Thread 对象。

方法摘要

static Thread currentThread()返回对当前正在执行的线程对象的引用。ClassLoader getContextClassLoader()返回该线程的上下文 ClassLoader。long getId()返回该线程的标识符。String getName()返回该线程的名称。int getPriority()返回线程的优先级。Thread.State getState()返回该线程的状态。ThreadGroup getThreadGroup()返回该线程所属的线程组。static boolean holdsLock(Object obj)当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。void interrupt()中断线程。

static boolean interrupted()测试当前线程是否已经中断。boolean isAlive()测试线程是否处于活动状态。boolean isDaemon()测试该线程是否为守护线程。boolean isInterrupted()测试线程是否已经中断。void join()等待该线程终止。void join(long millis)等待该线程终止的时间最长为 millis 毫秒。void join(long millis, int nanos)等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。void resume()已过时。该方法只与 suspend()一起使用,但 suspend()已经遭到反对,因为它具有死锁倾向。有关更多信息,请参阅为何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反对?。void run()如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。void setContextClassLoader(ClassLoader cl)设置该线程的上下文 ClassLoader。void setDaemon(boolean on)将该线程标记为守护线程或用户线程。

static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)设置当线程由于未捕获到异常而突然终止,并且没有为该线程定义其他处理程序时所调用的默认处理程序。void setName(String name)改变线程名称,使之与参数 name 相同。void setPriority(int newPriority)更改线程的优先级。

void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)设置该线程由于未捕获到异常而突然终止时调用的处理程序。static void sleep(long millis)在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。static void sleep(long millis, int nanos)在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行)。void start()使该线程开始执行;Java 虚拟机调用该线程的 run 方法。void stop()已过时。该方法具有固有的不安全性。用 Thread.stop 来终止线程将释放它已经锁定的所有监视器(作为沿堆栈向上传播的未检查 ThreadDeath 异常的一个自然后果)。如果以前受这些监视器保护的任何对象都处于一种不一致的状态,则损坏的对象将对其他线程可见,这有可能导致任意的行为。stop 的许多使用都应由只修改某些变量以指示目标线程应该停止运行的代码来取代。目标线程应定期检查该变量,并且如果该变量指示它要停止运行,则从其运行方法依次返回。如果目标线程等待很长时间(例如基于一个条件变量),则应使用 interrupt 方法来中断该等待。有关更多信息,请参阅《为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?》。void stop(Throwable obj)已过时。该方法具有固有的不安全性。请参阅 stop()以获得详细信息。该方法的附加危险是它可用于生成目标线程未准备处理的异常(包括若没有该方法该线程不太可能抛出的已检查的异常)。有关更多信息,请参阅为何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反对?。void suspend()已过时。该方法已经遭到反对,因为它具有固有的死锁倾向。如果目标线程挂起时在保护关键系统资源的监视器上保持有锁,则在目标线程重新开始以前任何线程都不能访问该资源。如果重新开始目标线程的线程想在调用 resume 之前锁定该监视器,则会发生死锁。这类死锁通常会证明自己是“冻结”的进程。有关更多信息,请参阅为何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反对?。String toString()返回该线程的字符串表示形式,包括线程名称、优先级和线程组。static void yield()暂停当前正在执行的线程对象,并执行其他线程。

六、线程的状态转换图

线程在一定条件下,状态会发生变化。线程变化的状态转换图如下:

1、新建状态(New):新创建了一个线程对象。

2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。

(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

七、线程的调度

1、调整线程优先级:Java线程有优先级,优先级高的线程会获得较多的运行机会。

Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量: static int MAX_PRIORITY 线程可以具有的最高优先级,取值为10。static int MIN_PRIORITY 线程可以具有的最低优先级,取值为1。static int NORM_PRIORITY 分配给线程的默认优先级,取值为5。

Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级。

每个线程都有默认的优先级。主线程的默认优先级为Thread.NORM_PRIORITY。

线程的优先级有继承关系,比如A线程中创建了B线程,那么B将和A具有相同的优先级。JVM提供了10个线程优先级,但与常见的操作系统都不能很好的映射。如果希望程序能移植到各个操作系统中,应该仅仅使用Thread类有以下三个静态常量作为优先级,这样能保证同样的优先级采用了同样的调度方式。

2、线程睡眠:Thread.sleep(long millis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。sleep()平台移植性好。

3、线程等待:Object类中的wait()方法,导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll()唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0)一样。

4、线程让步:Thread.yield()方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。

5、线程加入:join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。

6、线程唤醒:Object类中的notify()方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。类似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的所有线程。注意:Thread中suspend()和resume()两个方法在JDK1.5中已经废除,不再介绍。因为有死锁倾向。

7、常见线程名词解释

主线程:JVM调用程序mian()所产生的线程。

当前线程:这个是容易混淆的概念。一般指通过Thread.currentThread()来获取的进程。后台线程:指为其他线程提供服务的线程,也称为守护线程。JVM的垃圾回收线程就是一个后台线程。

前台线程:是指接受后台线程服务的线程,其实前台后台线程是联系在一起,就像傀儡和幕后操纵者一样的关系。傀儡是前台线程、幕后操纵者是后台线程。由前台线程创建的线程默认也是前台线程。可以通过isDaemon()和setDaemon()方法来判断和设置一个线程是否为后台线程。

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/27069

第二篇:多线程编程知识总结

多线程编程

一、问题的提出

1.1问题的引出

编写一个耗时的单线程程序:

新建一个基于对话框的应用程序SingleThread,在主对话框IDD_SINGLETHREAD_DIALOG添加一个按钮,ID为IDC_SLEEP_SIX_SECOND,标题为“延时6秒”,添加按钮的响应函数,代码如下:

void CSingleThreadDlg::OnSleepSixSecond(){ Sleep(6000);//延时6秒 } 编译并运行应用程序,单击“延时6秒”按钮,你就会发现在这6秒期间程序就象“死机”一样,不在响应其它消息。为了更好地处理这种耗时的操作,我们有必要学习——多线程编程。

1.2多线程概述

进程和线程都是操作系统的概念。进程是应用程序的执行实例,每个进程是由私有的虚拟地址空间、代码、数据和其它各种系统资源组成,进程在运行过程中创建的资源随着进程的终止而被销毁,所使用的系统资源在进程终止时被释放或关闭。

线程是进程内部的一个执行单元。系统创建好进程后,实际上就启动执行了该进程的主执行线程,主执行线程以函数地址形式,比如说main或WinMain函数,将程序的启动点提供给Windows系统。主执行线程终止了,进程也就随之终止。

每一个进程至少有一个主执行线程,它无需由用户去主动创建,是由系统自动创建的。用户根据需要在应用程序中创建其它线程,多个线程并发地运行于同一个进程中。一个进程中的所有线程都在该进程的虚拟地址空间中,共同使用这些虚拟地址空间、全局变量和系统资源,所以线程间的通讯非常方便,多线程技术的应用也较为广泛。

多线程可以实现并行处理,避免了某项任务长时间占用CPU时间。要说明的一点是,对于单处理器(CPU)的,为了运行所有这些线程,操作系统为每个独立线程安排一些CPU时间,操作系统以轮换方式向线程提供时间片,这就给人一种假象,好象这些线程都在同时运行。由此可见,如果两个非常活跃的线程为了抢夺对CPU的控制权,在线程切换时会消耗很多的CPU资源,反而会降低系统的性能。这一点在多线程编程时应该注意。

Win32 SDK函数支持进行多线程的程序设计,并提供了操作系统原理中的各种同步、互斥和临界区等操作。Visual C++中,使用MFC类库也实现了多线程的程序设计,使得多线程编程更加方便。1.3 Win32 API对多线程编程的支持

Win32 提供了一系列的API函数来完成线程的创建、挂起、恢复、终结以及通信等工作。下面将选取其中的一些重要函数进行说明。

1、HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);该函数在其调用进程的进程空间里创建一个新的线程,并返回已建线程的句柄,其中各参数说明如下:

lpThreadAttributes:指向一个 SECURITY_ATTRIBUTES 结构的指针,该结构决定了线程的安全属性,一般置为 NULL;

dwStackSize:指定了线程的堆栈深度,一般都设置为0;

lpStartAddress:表示新线程开始执行时代码所在函数的地址,即线程的起始地址。一般情况为(LPTHREAD_START_ROUTINE)ThreadFunc,ThreadFunc 是线程函数名;

lpParameter:指定了线程执行时传送给线程的32位参数,即线程函数的参数;

dwCreationFlags:控制线程创建的附加标志,可以取两种值。如果该参数为0,线程在被创建后就会立即开始执行;如果该参数为CREATE_SUSPENDED,则系统产生线程后,该线程处于挂起状态,并不马上执行,直至函数ResumeThread被调用;

lpThreadId:该参数返回所创建线程的ID;

如果创建成功则返回线程的句柄,否则返回NULL。

2、DWORD SuspendThread(HANDLE hThread);该函数用于挂起指定的线程,如果函数执行成功,则线程的执行被终止。

3、DWORD ResumeThread(HANDLE hThread);该函数用于结束线程的挂起状态,执行线程。

4、VOID ExitThread(DWORD dwExitCode);该函数用于线程终结自身的执行,主要在线程的执行函数中被调用。其中参数dwExitCode用来设置线程的退出码。

5、BOOL TerminateThread(HANDLE hThread,DWORD dwExitCode);

一般情况下,线程运行结束之后,线程函数正常返回,但是应用程序可以调用TerminateThread强行终止某一线程的执行。各参数含义如下: hThread:将被终结的线程的句柄;

dwExitCode:用于指定线程的退出码。

使用TerminateThread()终止某个线程的执行是不安全的,可能会引起系统不稳定;虽然该函数立即终止线程的执行,但并不释放线程所占用的资源。因此,一般不建议使用该函数。

6、BOOL PostThreadMessage(DWORD idThread,UINT Msg,WPARAM wParam,LPARAM lParam);该函数将一条消息放入到指定线程的消息队列中,并且不等到消息被该线程处理时便返回。idThread:将接收消息的线程的ID;

Msg:指定用来发送的消息;

wParam:同消息有关的字参数;

lParam:同消息有关的长参数;

调用该函数时,如果即将接收消息的线程没有创建消息循环,则该函数执行失败。

1.4.Win32 API多线程编程例程

例程1 [MultiThread1] 一个简单的线程。注意事项:

 Volatile:关键字:

volatile是要求C++编译器不要自作聪明的把变量缓冲在寄存器里.因为该变量可能会被意外的修改。(多个线程或其他原因)

如从串口读数据的场合,把变量缓冲在寄存器里,下次去读寄存器就没有意义了.因为串口的数据可能随时会改变的.加锁访问用于多个线程的场合.在进入临界区时是肯定要加锁的.volatile也加上,以保证从内存中读取变量的值. 终止线程:

Windows终止线程运行的四种方法 终止线程运行

若要终止线程的运行,可以使用下面的方法:

• 线程函数返回(最好使用这种方法)。

• 通过调用 ExitThread 函数,线程将自行撤消(最好不要使用这种方法)。

• 同一个进程或另一个进程中的线程调用 TerminateThread 函数(应该避免使用这种方法)。

• 包含线程的进程终止运行(应该避免使用这种方法)。

下面将介绍终止线程运行的方法,并且说明线程终止运行时会出现什么情况。

 线程函数返回

始终都应该将线程设计成这样的形式,即当想要线程终止运行时,它们就能够返回。这是确保所有线程资源被正确地清除的唯一办法。

如果线程能够返回,就可以确保下列事项的实现:

• 在线程函数中创建的所有 C++ 对象均将通过它们的撤消函数正确地撤消。

• 操作系统将正确地释放线程堆栈使用的内存。

• 系统将线程的退出代码(在线程的内核对象中维护)设置为线程函数的返回值。

• 系统将递减线程内核对象的使用计数。 使用 ExitThread 函数

可以让线程调用 ExitThread 函数,以便强制线程终止运行:

VOID ExitThread(DWORD dwExitCode);

该函数将终止线程的运行,并导致操作系统清除该线程使用的所有操作系统资源。但是,C++ 资源(如 C++ 类对象)将不被撤消。由于这个原因,最好从线程函数返回,而不是通过调用 ExitThread 来返回。

当然,可以使用 ExitThread 的 dwExitThread 参数告诉系统将线程的退出代码设置为什么。ExitThread 函数并不返回任何值,因为线程已经终止运行,不能执行更多的代码。 使用 TerminateThread 函数

调用 TerminateThread 函数也能够终止线程的运行:

BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode);

与 ExitThread 不同,ExitThread 总是撤消调用的线程,而 TerminateThread 能够撤消任何线程。hThread 参数用于标识被终止运行的线程的句柄。当线程终止运行时,它的退出代码成为你作为 dwExitCode 参数传递的值。同时,线程的内核对象的使用计数也被递减。

注意 TerminateThread 函数是异步运行的函数,也就是说,它告诉系统你想要线程终止运行,但是,当函数返回时,不能保证线程被撤消。如果需要确切地知道该线程已经终止运行,必须调用 WaitForSingleObject 或者类似的函数,传递线程的句柄。

设计良好的应用程序从来不使用这个函数,因为被终止运行的线程收不到它被撤消的通知。线程不能正确地清除,并且不能防止自己被撤消。

注意 当使用返回或调用 ExitThread 的方法撤消线程时,该线程的内存堆栈也被撤消。但是,如果使用 TerminateThread,那么在拥有线程的进程终止运行之前,系统不撤消该线程的堆栈。Microsoft故意用这种方法来实现 TerminateThread。如果其他仍然正在执行的线程要引用强制撤消的线程堆栈上的值,那么其他的线程就会出现访问违规的问题。如果将已经撤消的线程的堆栈留在内存中,那么其他线程就可以继续很好地运行。

此外,当线程终止运行时,DLL 通常接收通知。如果使用 TerminateThread 强迫线程终止,DLL 就不接收通知,这能阻止适当的清除,在进程终止运行时撤消线程。当线程终止运行时,会发生下列操作:

• 线程拥有的所有用户对象均被释放。在 Windows 中,大多数对象是由包含创建这些对象的线程的进程拥有的。但是一个线程拥有两个用户对象,即窗口和挂钩。当线程终止运行时,系统会自动撤消任何窗口,并且卸载线程创建的或安装的任何挂钩。其他对象只有在拥有线程的进程终止运行时才被撤消。

• 线程的退出代码从 STILL_ACTIVE 改为传递给 ExitThread 或 TerminateThread 的代码。

• 线程内核对象的状态变为已通知。

• 如果线程是进程中最后一个活动线程,系统也将进程视为已经终止运行。

• 线程内核对象的使用计数递减 1。

当一个线程终止运行时,在与它相关联的线程内核对象的所有未结束的引用关闭之前,该内核对象不会自动被释放。

一旦线程不再运行,系统中就没有别的线程能够处理该线程的句柄。然而别的线程可以调用 GetExitcodeThread 来检查由 hThread 标识的线程是否已经终止运行。如果它已经终止运行,则确定它的退出代码:

BOOL GetExitCodeThread(HANDLE hThread, PDOWRD pdwExitCode);退出代码的值在 pdwExitCode 指向的 DWORD 中返回。如果调用 GetExitCodeThread 时线程尚未终止运行,该函数就用 STILL_ACTIVE 标识符(定义为 0x103)填入 DWORD。如果该函数运行成功,便返回 TRUE。

 线程的定义:

例程2[MultiThread2] 传送一个一个整型的参数到一个线程中,以及如何等待一个线程完成处理。

DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);

hHandle:为要监视的对象(一般为同步对象,也可以是线程)的句柄;

dwMilliseconds:为hHandle对象所设置的超时值,单位为毫秒;

当在某一线程中调用该函数时,线程暂时挂起,系统监视hHandle所指向的对象的状态。如果在挂起的dwMilliseconds毫秒内,线程所等待的对象变为有信号状态,则该函数立即返回;如果超时时间已经到达dwMilliseconds毫秒,但hHandle所指向的对象还没有变成有信号状态,函数照样返回。参数dwMilliseconds有两个具有特殊意义的值:0和INFINITE。若为0,则该函数立即返回;若为INFINITE,则线程一直被挂起,直到hHandle所指向的对象变为有信号状态时为止。

例程3[MultiThread3] 传送一个结构体给一个线程函数,可以通过传送一个指向结构体的指针参数来完成。补充一点:如果你在void CMultiThread3Dlg::OnStart()函数中添加/* */语句,编译运行你就会发现进度条不进行刷新,主线程也停止了反应。什么原因呢?这是因为WaitForSingleObject函数等待子线程(ThreadFunc)结束时,导致了线程死锁。因为WaitForSingleObject函数会将主线程挂起(任何消息都得不到处理),而子线程ThreadFunc正在设置进度条,一直在等待主线程将刷新消息处理完毕返回才会检测通知事件。这样两个线程都在互相等待,死锁发生了,编程时应注意避免。

例程4[MultiThread4] 测试在Windows下最多可创建线程的数目。

二、MFC中的多线程开发

2.1 MFC对多线程编程的支持

MFC中有两类线程,分别称之为工作者线程和用户界面线程。二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环。

工作者线程没有消息机制,通常用来执行后台计算和维护任务,如冗长的计算过程,打印机的后台打印等。用户界面线程一般用于处理独立于其他线程执行之外的用户输入,响应用户及系统所产生的事件和消息等。但对于Win32的API编程而言,这两种线程是没有区别的,它们都只需线程的启动地址即可启动线程来执行任务。

在MFC中,一般用全局函数AfxBeginThread()来创建并初始化一个线程的运行,该函数有两种重载形式,分别用于创建工作者线程和用户界面线程。两种重载函数原型和参数分别说明如下:

(1)CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc,LPVOID pParam,nPriority=THREAD_PRIORITY_NORMAL,UINT nStackSize=0,DWORD dwCreateFlags=0,LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);

PfnThreadProc:指向工作者线程的执行函数的指针,线程函数原型必须声明如下: UINT ExecutingFunction(LPVOID pParam);请注意,ExecutingFunction()应返回一个UINT类型的值,用以指明该函数结束的原因。一般情况下,返回0表明执行成功。

pParam:传递给线程函数的一个32位参数,执行函数将用某种方式解释该值。它可以是数值,或是指向一个结构的指针,甚至可以被忽略;

nPriority:线程的优先级。如果为0,则线程与其父线程具有相同的优先级;

nStackSize:线程为自己分配堆栈的大小,其单位为字节。如果nStackSize被设为0,则线程的堆栈被设置成与父线程堆栈相同大小; dwCreateFlags:如果为0,则线程在创建后立刻开始执行。如果为CREATE_SUSPEND,则线程在创建后立刻被挂起;

lpSecurityAttrs:线程的安全属性指针,一般为NULL;

(2)CWinThread* AfxBeginThread(CRuntimeClass* pThreadClass,int nPriority=THREAD_PRIORITY_NORMAL,UINT nStackSize=0,DWORD dwCreateFlags=0,LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);

pThreadClass 是指向 CWinThread 的一个导出类的运行时类对象的指针,该导出类定义了被创建的用户界面线程的启动、退出等;其它参数的意义同形式1。使用函数的这个原型生成的线程也有消息机制,在以后的例子中我们将发现同主线程的机制几乎一样。下面对CWinThread类的数据成员及常用函数进行简要说明。

   m_hThread:当前线程的句柄;

m_nThreadID:当前线程的ID;

m_pMainWnd:指向应用程序主窗口的指针

virtual BOOL CWinThread::InitInstance();重载该函数以控制用户界面线程实例的初始化。初始化成功则返回非0值,否则返回0。用户界面线程经常重载该函数,工作者线程一般不使用InitInstance()。

virtual int CWinThread::ExitInstance();在线程终结前重载该函数进行一些必要的清理工作。该函数返回线程的退出码,0表示执行成功,非0值用来标识各种错误。同InitInstance()成员函数一样,该函数也只适用于用户界面线程。

2.2 MFC多线程编程实例

例程5 MultiThread5 为了与Win32 API对照,使用MFC 类库编程实现例程3 MultiThread3。

例程6 MultiThread6[用户界面线程]  创建用户界面线程的步骤:

1.使用ClassWizard创建类CWinThread的派生类(以CUIThread类为例)class CUIThread : public CWinThread { DECLARE_DYNCREATE(CUIThread)protected: CUIThread();// protected constructor used by dynamic creation

// Attributes public: // Operations public:

// Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CUIThread)public: virtual BOOL InitInstance();virtual int ExitInstance();//}}AFX_VIRTUAL // Implementation protected: virtual ~CUIThread();// Generated message map functions //{{AFX_MSG(CUIThread)

// NOTE-the ClassWizard will add and remove member functions here.//}}AFX_MSG

DECLARE_MESSAGE_MAP()};

2.重载函数InitInstance()和ExitInstance()。BOOL CUIThread::InitInstance(){ CFrameWnd* wnd=new CFrameWnd;wnd->Create(NULL,“UI Thread Window”);wnd->ShowWindow(SW_SHOW);wnd->UpdateWindow();m_pMainWnd=wnd;return TRUE;}

3.创建新的用户界面线程 void CUIThreadDlg::OnButton1(){

}

请注意以下两点:

A、在UIThreadDlg.cpp的开头加入语句: #include “UIThread.h” B、把UIThread.h中类CUIThread()的构造函数的特性由 protected 改为 public。CUIThread* pThread=new CUIThread();pThread->CreateThread();

用户界面线程的执行次序与应用程序主线程相同,首先调用用户界面线程类的InitInstance()函数,如果返回TRUE,继续调用线程的Run()函数,该函数的作用是运行一个标准的消息循环,并且当收到WM_QUIT消息后中断,在消息循环过程中,Run()函数检测到线程空闲时(没有消息),也将调用OnIdle()函数,最后Run()函数返回,MFC调用ExitInstance()函数清理资源。

你可以创建一个没有界面而有消息循环的线程,例如:你可以从CWinThread派生一个新类,在InitInstance函数中完成某项任务并返回FALSE,这表示仅执行InitInstance函数中的任务而不执行消息循环,你可以通过这种方法,完成一个工作者线程的功能。

三、线程间通讯

3.1通讯方式

一般而言,应用程序中的一个次要线程总是为主线程执行特定的任务,这样,主线程和次要线程间必定有一个信息传递的渠道,也就是主线程和次要线程间要进行通信。这种线程间的通信不但是难以避免的,而且在多线程编程中也是复杂和频繁的,下面将进行说明。

3.1.1使用全局变量进行通信

由于属于同一个进程的各个线程共享操作系统分配该进程的资源,故解决线程间通信最简单的一种方法是使用全局变量。对于标准类型的全局变量,建议使用volatile 修饰符,它告诉编译器无需对该变量作任何的优化,即无需将它放到一个寄存器中,并且该值可被外部改变。如果线程间所需传递的信息较复杂,可以定义一个结构,通过传递指向该结构的指针进行传递信息。

3.1.2使用自定义消息

可以在一个线程的执行函数中向另一个线程发送自定义的消息来达到通信的目的。一个线程向另外一个线程发送消息是通过操作系统实现的。利用Windows操作系统的消息驱动机制,当一个线程发出一条消息时,操作系统首先接收到该消息,然后把该消息转发给目标线程,接收消息的线程必须已经建立了消息循环。

3.2例程

例程GlobalObjectTest 该例程演示了如何利用全局变量进行通信

例程7[MultiThread7] 该例程演示了如何使用自定义消息进行线程间通信。首先,主线程向CCalculateThread线程发送消息WM_CALCULATE,CCalculateThread线程收到消息后进行计算,再向主线程发送WM_DISPLAY消息,主线程收到该消息后显示计算结果。步骤:

四、线程的同步

4.1基本概念

虽然多线程能给我们带来好处,但是也有不少问题需要解决。例如,对于像磁盘驱动器这样独占性系统资源,由于线程可以执行进程的任何代码段,且线程的运行是由系统调度自动完成的,具有一定的不确定性,因此就有可能出现两个线程同时对磁盘驱动器进行操作,从而出现操作错误;又例如,对于银行系统的计算机来说,可能使用一个线程来更新其用户数据库,而用另外一个线程来读取数据库以响应储户的需要,极有可能读数据库的线程读取的是未完全更新的数据库,因为可能在读的时候只有一部分数据被更新过。

使隶属于同一进程的各线程协调一致地工作称为线程的同步。MFC提供了多种同步对象,下面只介绍最常用的四种:

临界区(CCriticalSection)

事件(CEvent)

互斥量(CMutex)

信号量(CSemaphore)

通过这些类,可以比较容易地做到线程同步。

4.2使用 CCriticalSection 类

当多个线程访问一个独占性共享资源时,可以使用“临界区”对象。任一时刻只有一个线程可以拥有临界区对象,拥有临界区的线程可以访问被保护起来的资源或代码段,其他希望进入临界区的线程将被挂起等待,直到拥有临界区的线程放弃临界区时为止,这样就保证了不会在同一时刻出现多个线程访问共享资源。

CCriticalSection类的用法非常简单,步骤如下:

1.定义CCriticalSection类的一个全局对象(以使各个线程均能访问),如CCriticalSection critical_section;

2.在访问需要保护的资源或代码之前,调用CCriticalSection类的成员Lock()获得临界区对象: critical_section.Lock();3.在线程中调用该函数来使线程获得它所请求的临界区。如果此时没有其它线程占有临界区对象,则调用Lock()的线程获得临界区;否则,线程将被挂起,并放入到一个系统队列中等待,直到当前拥有临界区的线程释放了临界区时为止。

4.访问临界区完毕后,使用CCriticalSection的成员函数Unlock()来释放临界区:critical_section.Unlock();通俗讲,就是线程A执行到critical_section.Lock();语句时,如果其它线程(B)正在执行critical_section.Lock();语句后且critical_section.Unlock();语句前的语句时,线程A就会等待,直到线程B执行完critical_section.Unlock();语句,线程A才会继续执行。

例程8 MultiThread8 4.3使用 CEvent 类

CEvent 类提供了对事件的支持。事件是一个允许一个线程在某种情况发生时,唤醒另外一个线程的同步对象。例如在某些网络应用程序中,一个线程(记为A)负责监听通讯端口,另外一个线程(记为B)负责更新用户数据。通过使用CEvent 类,线程A可以通知线程B何时更新用户数据。每一个CEvent 对象可以有两种状态:有信号状态和无信号状态。线程监视位于其中的CEvent 类对象的状态,并在相应的时候采取相应的操作。

在MFC中,CEvent 类对象有两种类型:人工事件和自动事件。一个自动CEvent 对象在被至少一个线程释放后会自动返回到无信号状态;而人工事件对象获得信号后,释放可利用线程,但直到调用成员函数ReSetEvent()才将其设置为无信号状态。在创建CEvent 类的对象时,默认创建的是自动事件。CEvent 类的各成员函数的原型和参数说明如下:

1、CEvent(BOOL bInitiallyOwn=FALSE,BOOL bManualReset=FALSE,LPCTSTR lpszName=NULL,LPSECURITY_ATTRIBUTES lpsaAttribute=NULL);bInitiallyOwn:指定事件对象初始化状态,TRUE为有信号,FALSE为无信号;

bManualReset:指定要创建的事件是属于人工事件还是自动事件。TRUE为人工事件,FALSE为自动事件;

后两个参数一般设为NULL,在此不作过多说明。

2、BOOL CEvent::SetEvent();

将 CEvent 类对象的状态设置为有信号状态。如果事件是人工事件,则 CEvent 类对象保持为有信号状态,直到调用成员函数ResetEvent()将 其重新设为无信号状态时为止。如果CEvent 类对象为自动事件,则在SetEvent()将事件设置为有信号状态后,CEvent 类对象由系统自动重置为无信号状态。

如果该函数执行成功,则返回非零值,否则返回零。

3、BOOL CEvent::ResetEvent();

该函数将事件的状态设置为无信号状态,并保持该状态直至SetEvent()被调用时为止。由于自动事件是由系统自动重置,故自动事件不需要调用该函数。如果该函数执行成功,返回非零值,否则返回零。一般通过调用WaitForSingleObject函数来监视事件状态。前面已经介绍了该函数。由于语言描述的原因,CEvent 类的理解确实有些难度,只要通过下面例程,多看几遍就可理解。例程9 MultiThread9 仔细分析这两个线程函数, 就会正确理解CEvent 类。线程WriteD执行到 WaitForSingleObject(eventWriteD.m_hObject,INFINITE);处等待,直到事件eventWriteD为有信号该线程才往下执行,因为eventWriteD对象是自动事件,则当WaitForSingleObject()返回时,系统自动把eventWriteD对象重置为无信号状态。

4.4使用CMutex 类

互斥对象与临界区对象很像.互斥对象与临界区对象的不同在于:互斥对象可以在进程间使用,而临界区对象只能在同一进程的各线程间使用。当然,互斥对象也可以用于同一进程的各个线程间,但是在这种情况下,使用临界区会更节省系统资源,更有效率。

4.5使用CSemaphore 类

当需要一个计数器来限制可以使用某个线程的数目时,可以使用“信号量”对象。CSemaphore 类的对象保存了对当前访问某一指定资源的线程的计数值,该计数值是当前还可以使用该资源的线程的数目。如果这个计数达到了零,则所有对这个CSemaphore 类对象所控制的资源的访问尝试都被放入到一个队列中等待,直到超时或计数值不为零时为止。一个线程被释放已访问了被保护的资源时,计数值减1;一个线程完成了对被控共享资源的访问时,计数值增1。这个被CSemaphore 类对象所控制的资源可以同时接受访问的最大线程数在该对象的构建函数中指定。

CSemaphore 类的构造函数原型及参数说明如下:

CSemaphore(LONG lInitialCount=1,LONG lMaxCount=1,LPCTSTR pstrName=NULL,LPSECURITY_ATTRIBUTES lpsaAttributes=NULL);lInitialCount:信号量对象的初始计数值,即可访问线程数目的初始值;

lMaxCount:信号量对象计数值的最大值,该参数决定了同一时刻可访问由信号量保护的资源的线程最大数目;

后两个参数在同一进程中使用一般为NULL,不作过多讨论;

在用CSemaphore 类的构造函数创建信号量对象时要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会减1,只要当前可用资源计数是大于0的,就可以发出信号量信号。但是当前可用计数减小到0时,则说明当前占用资源的线程数已经达到了所允许的最大数目,不能再允许其它线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资源数加1。例程10 MultiThread10 为了文件中能够正确使用同步类,在文件开头添加: #include “afxmt.h” 定义信号量对象和一个字符数组,为了能够在不同线程间使用,定义为全局变量:CSemaphore semaphoreWrite(2,2);//资源最多访问线程2个,当前可访问线程数2个

在信号量对象有信号的状态下,线程执行到WaitForSingleObject语句处继续执行,同时可用线程数减1;若线程执行到WaitForSingleObject语句时信号量对象无信号,线程就在这里等待,直到信号量对象有信号线程才往下执行。

第三篇:C++多线程编程简单实例

C++多线程编程简单实例(2012-05-18 16:26:01)

转载▼

标签:

it

C++本身并没有提供任何多线程机制,但是在windows下,我们可以调用SDK win32 api来编写多线程的程序,下面就此简单的讲一下:

创建线程的函数

HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD

SIZE_T dwStackSize,// initial stack size

LPTHREAD_START_ROUTINE lpStartAddress,// thread function

LPVOID lpParameter,// thread argument

DWORD dwCreationFlags,// creation option

LPDWORD lpThreadId// thread identifier);

在这里我们只用到了第三个和第四个参数,第三个参数传递了一个函数的地址,也是我们要指定的新的线程,第四个参数是传给新线程的参数指针。

eg1:

#include

#include

using namespace std;

DWORD WINAPI Fun(LPVOID lpParamter)

{

while(1){ cout<<“Fun display!”<

}

int main()

{

HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);

CloseHandle(hThread);

while(1){ cout<<“main display!”<

return 0;

}

我们可以看到主线程(main函数)和我们自己的线程(Fun函数)是随机地交替执行的,但是两个线程输出太快,使我们很难看清楚,我们可以使用函数

VOID Sleep(DWORD dwMilliseconds// sleep time);

来暂停线程的执行,dwMilliseconds表示千分之一秒,所以

Sleep(1000);

表示暂停1秒

eg2:

#include

#include

using namespace std;

DWORD WINAPI Fun(LPVOID lpParamter)

{

while(1){ cout<<“Fun display!”<

}

int main()

{

HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);

CloseHandle(hThread);

while(1){ cout<<“main display!”<

return 0;

}

执行上述代码,这次我们可以清楚地看到在屏幕上交错地输出Fun display!和main display!,我们发现这两个函数确实是并发运行的,细心的读者可能会发现我们的程序是每当Fun函数和main函数输出内容后就会输出换行,但是我们看到的确是有的时候程序输出换行了,有的时候确没有输出换行,甚至有的时候是输出两个换行。这是怎么回事?下面我们把程序改一下看看:

eg3:

#include

#include

using namespace std;

DWORD WINAPI Fun(LPVOID lpParamter)

{

while(1){ cout<<“Fun display!n”;Sleep(1000);}

}

int main()

{

HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);

CloseHandle(hThread);

while(1){ cout<<“main display!n”;Sleep(2000);}

return 0;

}

我们再次运行这个程序,我们发现这时候正如我们预期的,正确地输出了我们想要输出的内容并且格式也是正确的。下面我就来讲一下此前我们的程序为什么没有正确的运行。多线程的程序时并发地运行的,多个线程之间如果公用了一些资源的话,我们并不能保证这些资源都能正确地被利用,因为这个时候资源并不是独占的,举个例子吧:

eg4:

加入有一个资源 int a = 3

有一个线程函数 selfAdd()该函数是使 a += a;

又有一个线程函数 selfSub()该函数是使a-= a;

我们假设上面两个线程正在并发欲行,如果selfAdd在执行的时候,我们的目的是想让a编程6,但此时selfSub得到了运行的机会,所以a变成了0,等到selfAdd的到执行的机会后,a += a,但是此时a确是0,并没有如我们所预期的那样的到6,我们回到前面EG2,在这里,我们可以把屏幕看成是一个资源,这个资源被两个线程所共用,加入当Fun函数输出了Fun display!后,将要输出endl(也就是清空缓冲区并换行,在这里我们可以不用理

解什么事缓冲区),但此时main函数确得到了运行的机会,此时Fun函数还没有来得及输出换行就把CPU让给了main函数,而这时main函数就直接在Fun display!后输出main display!,至于为什么有的时候程序会连续输出两个换行,读者可以采用同样的分析方法来分析,在这里我就不多讲了,留给读者自己思考了。

那么为什么我们把eg2改成eg3就可以正确的运行呢?原因在于,多个线程虽然是并发运行的,但是有一些操作是必须一气呵成的,不允许打断的,所以我们看到eg2和eg3的运行结果是不一样的。

那么,是不是eg2的代码我们就不可以让它正确的运行呢?答案当然是否,下面我就来讲一下怎样才能让eg2的代码可以正确运行。这涉及到多线程的同步问题。对于一个资源被多个线程共用会导致程序的混乱,我们的解决方法是只允许一个线程拥有对共享资源的独占,这样就能够解决上面的问题了。

HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,// SD

BOOL bInitialOwner,// initial owner

LPCTSTR lpName// object name);

该函数用于创造一个独占资源,第一个参数我们没有使用,可以设为NULL,第二个参数指定该资源初始是否归属创建它的进程,第三个参数指定资源的名称。

HANDLE hMutex = CreateMutex(NULL,TRUE,“screen”);

这条语句创造了一个名为screen并且归属于创建它的进程的资源

BOOL ReleaseMutex(HANDLE hMutex// handle to mutex);

该函数用于释放一个独占资源,进程一旦释放该资源,该资源就不再属于它了,如果还要用到,需要重新申请得到该资源。申请资源的函数如下

DWORD WaitForSingleObject(HANDLE hHandle,// handle to object

DWORD dwMilliseconds// time-out interval);

第一个参数指定所申请的资源的句柄,第二个参数一般指定为INFINITE,表示如果没有申请到资源就一直等待该资源,如果指定为0,表示一旦得不到资源就返回,也可以具体地指定等待多久才返回,单位是千分之一秒。好了,该到我们来解决eg2的问题的时候了,我们可以把eg2做一些修改,如下

eg5:

#include

#include

using namespace std;

HANDLE hMutex;

DWORD WINAPI Fun(LPVOID lpParamter)

{

while(1){

WaitForSingleObject(hMutex, INFINITE);

cout<<“Fun display!”<

Sleep(1000);

ReleaseMutex(hMutex);

}

}

int main()

{

HANDLE hThread = CreateThread(NULL, 0, Fun, NULL, 0, NULL);hMutex = CreateMutex(NULL, FALSE, “screen”);

CloseHandle(hThread);

while(1){

WaitForSingleObject(hMutex, INFINITE);

cout<<“main display!”<

Sleep(2000);

ReleaseMutex(hMutex);

}

return 0;

}

运行代码正如我们所预期的输出的内容。

第四篇:Java编程

《Java编程》

计算器

班级:****** 姓名:******

学号: ******* 指导老师:******

实验名称:JAVA计算器

1实验目的: Java编程语言在编程方面的具体应用,以及使用面向对象方法,对小应用程序进行需求分

析、概要设计、详细设计,最后使用Java编程实现的全过程。

2实验意义:

在编程我们使用的java语言,是目前比较流行的编程语言。在当今这个时代,java语言在编程方面的优势使得编程有了更好的选择。Java语言最大的特点是具有跨平台性,使其不受平台不同的影响,得到了广泛的应用。实训性质

本课程是计算机信息管理专业的一门实践性课程,是《Java编程》课程的实践性教学环节。实训目标

⑴综合应用JAVA程序设计的知识解决实际问题。

⑵学会在应用程序的设计过程中,应用面向对象的程序设计方法。⑶学会应用JDBC创建数据库应用程序。

⑷学会开发基于Swing的应用程序及多文档应用程序的设计。实训任务

用Java语言开发工具(例如JDK、Jcreator、NetBeans等)制作一个简单的可运行的完整的应用程序或小型系统,并编制出各阶段必要的文档。

将创建一个计算器,可以进行常用的加减乘除算术运算。本实例的知识点有:窗口布局器Gridlayout的应用,对按钮消息的监听和响应。

6实训条件

<软件:>Windows XP,NetBeans IDE 6.52 7开发背景: Java是由Sun Microsystems公司于1995年5月推出的Java程序设计语言(以下简称Java语言)和Java平台的总称。Java语言是一个支持网络计算的面向对象程序设计语言。Java语言吸收了Smalltalk语言和C++语言的优点,并增加了其它特性,如支持并发程序设计、网络通信、和多媒体数据控制等。

8系统部分分析:

1)Java语言是简单的。Java语言的语法与C语言和C++语言很接近,使得大多数程序员很容易学习和使用Java。另一方面,Java丢弃了C++ 中很少使用的、很难理解的、令人迷惑的那些特性,如操作符重载、多继承、自动的强制类型转换。

2)Java语言是一个面向对象的。Java语言提供类、接口和继承等原语,为了简单起见,只支持类之间的单继承,但支持接口之间的多继承,并支持类与接口之间的实现机制(关键字为implements)。Java语言全面支持动态绑定,而C++ 语言只对虚函数使用动态绑定

3)Java语言是分布式的。Java语言支持Internet应用的开发,在基本的Java应用编程接口中有一个网络应用编程接口(java.net),它提供了用于网络应用编程的类库,包括URL、URLConnection、Socket、ServerSocket等。Java的RMI(远程方法激活)机制也是开发分布式应用的重要手段。

4)Java语言是健壮的。Java的强类型机制、异常处理、废料的自动收集等是Java程序健壮性的重要保证。对指针的丢弃是Java的明智选择。Java的安全检查机制使得Java更具健壮性。

5)Java语言是安全的。Java通常被用在网络环境中,为此,Java提供了一个安全机制以防恶意代码的攻击。除了Java语言具有的许多安全特性以外,Java对通过网络下载的类具有一个安全防范机制(类ClassLoader),如分配不同的名字空间以防替代本地的同名类、字节代码检查,并提供安全管理机制.6)Java语言是体系结构中立的。Java程序(后缀为java的文件)在Java平台上被编译为体系结构中立的字节码格式(后缀为class的文件), 然后可以在实现这个Java平台的任何系统中运行。

7)Java语言是可移植的。这种可移植性来源于体系结构中立性,另外,Java还严格规定了各个基本数据类型的长度。Java系统本身也具有很强的可移植性,Java编译器是用Java实现的.8)Java语言是解释型的。如前所述,Java程序在Java平台上被编译为字节码格式,然后可以在实现这个Java平台的任何系统中运行。

9)Java是高性能的。与那些解释型的高级脚本语言相比,Java的确是高性能的。事实上,Java的运行速度随着JIT(Just-In-Time)编译器技术的发展越来越接近于C++。

10)Java语言是多线程的。在Java语言中,线程是一种特殊的对象,它必须由Thread类或其子(孙)类来创建。

11)Java语言是动态的。Java语言的设计目标之一是适应于动态变化的环境。

目录

课程设计题目 ……………………………… p1

课程设计简介 ……………………………… p2

课程设计源代码…………………………… p5

课程设计运行结果 ……………………… p15 课程设计心得体会 ………………………

p16

package computerpad;import java.awt.*;import java.awt.event.*;import javax.swing.*;import javax.swing.border.*;import java.util.LinkedList;import java.text.NumberFormat;public class ComputerPad extends Frame implements ActionListener {

NumberButton numberButton[];

OperationButton oprationButton[];

Button 小数点按钮,正负号按钮,退格按钮,求倒数按钮,等号按钮,清零按钮;

Panel panel;

JTextField resultShow;

String 运算符号[]={“+”,“-”,“*”,“/”};

LinkedList 链表;

boolean 是否按下等号=false;

public ComputerPad()

{

super(“计算器”);

链表=new LinkedList();

numberButton=new NumberButton[10];

for(int i=0;i<=9;i++)

{

numberButton[i]=new NumberButton(i);

numberButton[i].addActionListener(this);

}

oprationButton=new OperationButton[4];

for(int i=0;i<4;i++)

{

oprationButton[i]=new OperationButton(运算符号[i]);

oprationButton[i].addActionListener(this);

}

小数点按钮=new Button(“.”);

正负号按钮

=new Button(“+/-”);

等号按钮=new Button(“=”);

求倒数按钮=new Button(“1/x”);

退格按钮=new Button(“退格”);

清零按钮=new Button(“C”);

清零按钮.setForeground(Color.red);

退格按钮.setForeground(Color.red);

等号按钮.setForeground(Color.red);

求倒数按钮.setForeground(Color.blue);

正负号按钮.setForeground(Color.blue);

小数点按钮.setForeground(Color.blue);

退格按钮.addActionListener(this);

清零按钮.addActionListener(this);

等号按钮.addActionListener(this);

小数点按钮.addActionListener(this);

正负号按钮.addActionListener(this);

求倒数按钮.addActionListener(this);

resultShow=new JTextField(10);

resultShow.setHorizontalAlignment(JTextField.RIGHT);

resultShow.setForeground(Color.blue);

resultShow.setFont(new Font(“TimesRoman”,Font.PLAIN,14));

resultShow.setBorder(new SoftBevelBorder(BevelBorder.LOWERED));

resultShow.setBackground(Color.white);

resultShow.setEditable(false);

panel=new Panel();

panel.setLayout(new GridLayout(4,5));

panel.add(numberButton[1]);

panel.add(numberButton[2]);

panel.add(numberButton[3]);

panel.add(oprationButton[0]);

panel.add(清零按钮);

panel.add(numberButton[4]);

panel.add(numberButton[5]);

panel.add(numberButton[6]);

panel.add(oprationButton[1]);

panel.add(退格按钮);

panel.add(numberButton[7]);

panel.add(numberButton[8]);

panel.add(numberButton[9]);

panel.add(oprationButton[2]);

panel.add(求倒数按钮);

panel.add(numberButton[0]);

panel.add(正负号按钮);

panel.add(小数点按钮);

panel.add(oprationButton[3]);

panel.add(等号按钮);

add(panel,BorderLayout.CENTER);

add(resultShow,BorderLayout.NORTH);

addWindowListener(new WindowAdapter()

{ public void windowClosing(WindowEvent e)

{

System.exit(0);

}

});

setVisible(true);

setBounds(100,50,240,180);

setResizable(false);

validate();

} public void actionPerformed(ActionEvent e)

{

if(e.getSource()instanceof NumberButton)

{

NumberButton b=(NumberButton)e.getSource();

if(链表.size()==0)

{

int number=b.getNumber();

链表.add(“"+number);

resultShow.setText(”“+number);

是否按下等号=false;

}

else if(链表.size()==1&&是否按下等号==false)

{

int number=b.getNumber();

String num=(String)链表.getFirst();

String s=num.concat(”“+number);

链表.set(0,s);

resultShow.setText(s);

}

else if(链表.size()==1&&是否按下等号==true)

{

int number=b.getNumber();

链表.removeFirst();

链表.add(”“+number);

是否按下等号=false;

resultShow.setText(”“+number);

}

else if(链表.size()==2)

{

int number=b.getNumber();

链表.add(”“+number);

resultShow.setText(”“+number);

}

else if(链表.size()==3)

{

int number=b.getNumber();

String num=(String)链表.getLast();

String s=num.concat(”“+number);

链表.set(2,s);

resultShow.setText(s);

}

}

else if(e.getSource()instanceof OperationButton)

{

OperationButton b=(OperationButton)e.getSource();

if(链表.size()==1)

{

String fuhao=b.get运算符号();

链表.add(fuhao);

}

else if(链表.size()==2)

{

String fuhao=b.get运算符号();

链表.set(1,fuhao);

}

else if(链表.size()==3)

{

String fuhao=b.get运算符号();

String number1=(String)链表.getFirst();

String number2=(String)链表.getLast();

String 运算符号=(String)链表.get(1);

try

{

double n1=Double.parseDouble(number1);

double n2=Double.parseDouble(number2);

double n=0;

if(运算符号.equals(”+“))

{

n=n1+n2;

}

else if(运算符号.equals(”-“))

{

n=n1-n2;

}

else if(运算符号.equals(”*“))

{

n=n1*n2;

}

else if(运算符号.equals(”/“))

{

n=n1/n2;

}

链表.clear();

链表.add(”“+n);

链表.add(fuhao);

resultShow.setText(”“+n);

}

catch(Exception ee)

{

}

}

}

else if(e.getSource()==等号按钮)

{

是否按下等号=true;

if(链表.size()==1||链表.size()==2)

{

String num=(String)链表.getFirst();

resultShow.setText(”“+num);

}

else if(链表.size()==3)

{

String number1=(String)链表.getFirst();

String number2=(String)链表.getLast();

String 运算符号=(String)链表.get(1);

try

{

double n1=Double.parseDouble(number1);

double n2=Double.parseDouble(number2);

double n=0;

if(运算符号.equals(”+“))

{

n=n1+n2;

}

else if(运算符号.equals(”-“))

{

n=n1-n2;

}

else if(运算符号.equals(”*“))

{

n=n1*n2;

}

else if(运算符号.equals(”/“))

{

n=n1/n2;

}

resultShow.setText(”“+n);

链表.set(0,”“+n);

链表.removeLast();

链表.removeLast();

}

catch(Exception ee)

{

}

}

}

else if(e.getSource()==小数点按钮)

{

if(链表.size()==0)

{

是否按下等号=false;

}

else if(链表.size()==1)

{

String dot=小数点按钮.getLabel();

String num=(String)链表.getFirst();

String s=null;

if(num.indexOf(dot)==-1)

{

s=num.concat(dot);

链表.set(0,s);

}

else

{

s=num;

}

链表.set(0,s);

resultShow.setText(s);

}

else if(链表.size()==3)

{

String dot=小数点按钮.getLabel();

String num=(String)链表.getLast();

String s=null;

if(num.indexOf(dot)==-1)

{

s=num.concat(dot);

链表.set(2,s);

}

else

{

s=num;

}

resultShow.setText(s);

}

}

else if(e.getSource()==退格按钮)

{

if(链表.size()==1)

{

String num=(String)链表.getFirst();

if(num.length()>=1)

{

num=num.substring(0,num.length()-1);

链表.set(0,num);

resultShow.setText(num);

}

else

{

链表.removeLast();

resultShow.setText(”0“);

}

}

else if(链表.size()==3)

{

String num=(String)链表.getLast();

if(num.length()>=1)

{ num=num.substring(0,num.length()-1);

链表.set(2,num);

resultShow.setText(num);

}

else

{

链表.removeLast();

resultShow.setText(”0“);

}

}

}

else if(e.getSource()==正负号按钮)

{

if(链表.size()==1)

{

String number1=(String)链表.getFirst();

try

{

double d=Double.parseDouble(number1);

d=-1*d;

String str=String.valueOf(d);

链表.set(0,str);

resultShow.setText(str);

}

catch(Exception ee)

{

}

}

else if(链表.size()==3)

{

String number2=(String)链表.getLast();

try

{

double d=Double.parseDouble(number2);

d=-1*d;

String str=String.valueOf(d);

链表.set(2,str);

resultShow.setText(str);

}

catch(Exception ee){

}

}

}

else if(e.getSource()==求倒数按钮)

{

if(链表.size()==1||链表.size()==2)

{

String number1=(String)链表.getFirst();

try

{

double d=Double.parseDouble(number1);

d=1.0/d;

String str=String.valueOf(d);

链表.set(0,str);

resultShow.setText(str);

}

catch(Exception ee){

}

}

else if(链表.size()==3)

{

String number2=(String)链表.getLast();

try

{

double d=Double.parseDouble(number2);

d=1.0/d;

String str=String.valueOf(d);

链表.set(0,str);

resultShow.setText(str);

}

catch(Exception ee){

}

}

}

else if(e.getSource()==清零按钮)

{

是否按下等号=false;

resultShow.setText(”0“);

链表.clear();

}

} public static void main(String args[])

{

new ComputerPad();

}

}

package computerpad;import java.awt.*;import java.awt.event.*;import javax.swing.*;public class NumberButton extends Button {

int number;

public NumberButton(int number)

{

super(”"+number);

this.number=number;

setForeground(Color.blue);

}

public int getNumber()

{

return number;

} }

import java.awt.*;import java.awt.event.*;import javax.swing.*;public class OperationButton extends Button {

String 运算符号;

public OperationButton(String s)

{

super(s);

运算符号=s;

setForeground(Color.red);

}

public String get运算符号()

{

return 运算符号;

} } 14 Java实训心得:

未接触Java之前,听人说Java这门语言如何的强大和难以入门,但学习之后,给我的感觉却是语言没有所谓的难于不难,关键是自己有没有真正投入去学,有没有花时间去学。Java是一门很好的语言,经过周围人对Java的宣传,我一开始不敢去学习这门语言,因为一门高级语言总是让人想到一开始的学习会很难,但是后来在自己的努力和老师同学的帮助下,我加入了Java学习者的行列。

老师把我们带进了门,那么,以后漫长的深入学习还是要靠自己。经常性的编写一些程序,或则去看懂、研究透别人编写的程序对于我们打好基础是非常有利的。让我们怀着对Java的一腔热情,用自己的刻苦努力去把Java学好。将来,用自己的成绩去回报有恩于我们的社会、家人和朋友。

第五篇:java编程

in the following code, which is the earliest statement, where the object originally held in e, may be garbage collected:

1.public class test {

2.public static void main(string args []){

3.employee e = new employee(“bob”, 48);

4.e.calculatepay();

5.system.out.println(e.printdetails());

6.e = null;

7.e = new employee(“denise”, 36);

8.e.calculatepay();

9.system.out.println(e.printdetails());

10.}

11.}

only one:

in the following code, which is the earliest statement, where the object originally held in e, may be garbage collected:

1.public class test {

2.public static void main(string args []){

3.employee e = new employee(“bob”, 48);

4.e.calculatepay();

5.system.out.println(e.printdetails());

6.e = null;

7.e = new employee(“denise”, 36);

8.e.calculatepay();

9.system.out.println(e.printdetails());

10.}

11.}

only one:

a.line 10

b.line 11

c.line 7

d.line 8

2:exhibit :

1.public class test(2.private static int j = 0;

3.4.private static boolean methodb(int k)(5.j += k;

6.return true;

6.)

7.8.public static void methoda(int i){

9.boolean b:

10.b = i < 10 | methodb(4);

11.b = i < 10 || methodb(8);

12.)

13.14.public static void main(string args[] }(15.methoda(0);

16.system.out.printin(j);

17.)

18.)

what is the result?

a.the program prints “0”

b.the program prints “4”

c.the program prints “8”

d.the program prints “12”

3:what is written to the standard output given the following statement:system.out.println(4|7);

select the right answer:

a.4

b.5

c.6

d.7

4:

select valid identifier of java:

select valid identifier of java:

a.%passwd

b.3d_game

c.$charge

d.this

5:设有变量说明语句int a=1,b=0;

则执行以下程序段的输出结果为()。

switch(a)

{

case 1:

switch(b)

{

case 0:printf(“**0**”);break;

case 1:printf(“**1**”);break;

}

case 2:printf(“**2**”);break;

}

printf(“ ”);

a.**0**

b.**0****2**

c.**0****1****2**

d.有语法错误

6:in the following pieces of code, which one will compile without any error?

a.stringbuffer sb1 = “abcd”;

b.boolean b = new boolean(“abcd”);

c.c: byte b = 255;

d.float fl = 1.2;

7:

what is the result when you compile and run the following code?

public class throwsdemo

{

static void throwmethod()

{

system.out.println(“inside throwmethod.”);

throw new illegalaccessexception(“demo”);

}

public static void main(string args[])

{

try

{

throwmethod();

}

catch(illegalaccessexception e)

{

system.out.println(“caught ” + e);

}

}

}

choices:

what is the result when you compile and run the following code?

public class throwsdemo

{

static void throwmethod()

{

system.out.println(“inside throwmethod.”);

throw new illegalaccessexception(“demo”);

}

public static void main(string args[])

{

try

{

throwmethod();

}

catch(illegalaccessexception e)

{

system.out.println(“caught ” + e);

}

}

}

choices:

a.compilation error

b.runtime error

c.compile successfully, nothing is printed.d.inside throwmethod.followed by caught:java.lang.illegalaccessexcption: demo

8:which of the following statements are not legal?

a.long l = 4990;

b.int i = 4l;

c.double d = 34.4;

d.double t = 0.9f.9:

give the following java class:

public class example{

public static void main(string args[]){

static int x[] = new int[15];

system.out.println(x[5]);

}

}

which statement is corrected?

give the following java class:

public class example{

public static void main(string args[]){

static int x[] = new int[15];

system.out.println(x[5]);

}

}

which statement is corrected?

a.when compile, some error will occur.b.when run, some error will occur.c.output is zero.d.output is null.10:下面关于变量及其范围的陈述哪些是错的。

a.实例变量是类的成员变量。

b.实例变量用关键字static声明。

c.在方法中定义的局部变量在该方法被执行时创建

d.局部变量在使用前必须被初始化。

11:

public class x{

public object m(){

object o = new float(3.14f);//line 3

object [] oa = new object[1];//line 4

oa[0] = o;//line 5

o=null;//line 6

return oa[0];//line 7

}

}

when is the float object, created in line 3,eligible for garbage collection?

public class x{

public object m(){

object o = new float(3.14f);//line 3

object [] oa = new object[1];//line 4

oa[0] = o;//line 5

o=null;//line 6

return oa[0];//line 7

}

}

when is the float object, created in line 3,eligible for garbage collection?

a.just after line 5.b.just after line 6

c.just after line 7(that is,as the method returns)

d.never in this method

12:

which is the most appropriate code snippet that can be inserted at line 18 in the following code?

(assume that the code is compiled and run with assertions enabled)

1.import java.util.*;

2.3.public class asserttest

4.{

5.private hashmap cctld;

6.7.public asserttest()

8.{

9.cctld = new hashmap();

10.cctld.put(“in”, “india”);

11.cctld.put(“uk”, “united kingdom”);

12.cctld.put(“au”, “australia”);

13.// more code...14.}

15.// other methods....16.public string getcountry(string countrycode)

17.{

18.// what should be inserted here?

19.string country =(string)cctld.get(countrycode);

20.return country;

21.}

22.}

which is the most appropriate code snippet that can be inserted at line 18 in the following code?

(assume that the code is compiled and run with assertions enabled)

1.import java.util.*;

2.3.public class asserttest

4.{

5.private hashmap cctld;

6.7.public asserttest()

8.{

9.cctld = new hashmap();

10.cctld.put(“in”, “india”);

11.cctld.put(“uk”, “united kingdom”);

12.cctld.put(“au”, “australia”);

13.// more code...14.}

15.// other methods....16.public string getcountry(string countrycode)

17.{

18.// what should be inserted here?

19.string country =(string)cctld.get(countrycode);

20.return country;

21.}

22.}

a.assert countrycode!= null;

b.assert countrycode!= null : “country code can not be null”;

c.assert cctld!= null : “no country code data is available”;

d.assert cctld : “no country code data is available”;

13:

give the following code:

public class example{

public static void main(string args[]){

int l=0;

do{

system.out.println(“doing it for l is:”+l);

}while(—l>0)

system.out.println(“finish”);

}

}

which well be output:

give the following code:

public class example{

public static void main(string args[]){

int l=0;

do{

system.out.println(“doing it for l is:”+l);

}while(—l>0)

system.out.println(“finish”);

}

}

which well be output:

a.doing it for l is 3

b.doing it for l is 1

c.doing it for l is 2

d.doing it for l is 0

14:which statements about java code security are not true?

a.the bytecode verifier loads all classes needed for the execution of a program.b.executing code is performed by the runtime interpreter.c.at runtime the bytecodes are loaded, checked and run in an interpreter.d.the class loader adds security by separating the namespaces for the classes of the local file system from those imported from network sources.15:a class design requires that a member variable should be accessible only by same package, which modifer word should be used?

a.protected

b.public

c.no modifer

d.private

16:character流与byte流的区别是

a.每次读入的字节数不同

b.前者带有缓冲,后者没有

c.前者是块读写,后者是字节读写

d.二者没有区别,可以互换使用

简答题

17:找出两个字符串中最大子字符串,如“abractyeyt”,“dgdsaeactyey”的最大子串为“actyet”

18:假设你有一个用1001个整数组成的数组,这些整数是任意排列的,但是你知道所有的整数都在1到1000(包括1000)之间。此外,除一个数字出现两次外,其他所有数字只出现一次。假设你只能对这个数组做一次处理,用一种算法找出重复的那个数字。如果你在运算中使用了辅助的存储方式,那么你能找到不用这种方式的算法吗?

19:到底在哪里使用cascade=“...”?

20:使用tomcat部署应用程序 遇到过java.lang.outofmemoryerror 吗?如何解决的。

21:请写一个java程序实现数据库缓冲池的功能?

22:有200个正整数,且每个数均在1000至9999之间。请编制函数,其函数的功能是:要求按每个数的后三位的大小进行升序排列,然后取出满足此条件的前10个数依次存入数组bb中,如果后三位的数值相等,则按原先的数值进行降序排列。

23:anonymous inner class(匿名内部类)是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)?

24:找出字符串a中包含的字符可以进行的所有不同组合。例如:abccd中,ab,ac,bc,cc,abd等都是可能的组合。

25:下面的代码在绝大部分时间内都运行得很正常,请问在什么情况下会出现问题?问题的根源在哪里?

import java.util.linkedlist;

public class stack {

linkedlist list = new linkedlist();

public synchronized void push(object x){

synchronized(list){

list.addlast(x);

notify();

}

}

public synchronized object pop()

throws exception {

synchronized(list){

if(list.size()<= 0){

wait();

}

return list.removelast();

}

}

}

Java多线程编程总结(★)
TOP