1、第9章 多线程,目录,9.1 理解多线程 9.2 线程优先级 9.3 多线程的实现 9.4 多线程的同步 9.5 综合实例 9.6 习题,9.1 理解多线程,9.1.1 线程与进程的概念进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。比如在Windows系统中,一个运行的exe就是一个进程。 如图9-1 Windows进程状况所示:,图9-1 Windows进程状况,线程是指进程中的一个执行流程,一个进程中可以运行多个线程。比如 java.exe进程中可以运行很多线程。线程总是属于某个进程,进程中的多个线程共享进程的内存。如图9-2 进程与线程
2、的关系所示:,9.1.2多线程的基本概念 多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。 多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”。如果系统只有一个CPU,那么真正的“同时”是不可能的,但是由于CPU的速度非常快,用户感觉不到其中的区别,因此,我们也不用关心它,只需要设想各个线程是同时执行即可。 多线程和传统的单线程在程序设计上最大的区别在于,由于各个线程的控制流彼此独立,使得各个线程之间的代码是乱序执行的。,9.1.3 线程的状态一个线程对象从创建、启动、运行、终止,直到线程对象被java虚拟机所释放,其
3、生命周期会处于各种不同的状态,如图9-3 线程的状态转换所示:,图9-3 线程的状态转换,【实例9-1】线程状态判断。Judge.java import java.util.Date; public class Judge extends Thread int sleepTime;String name;int counter;public test(int x, String n) sleepTime = x;name = n;counter = 0;public void run() while (counter 3) try counter+;System.out.println(nam
4、e + “:“+ new Date(System.currentTimeMillis();Thread.sleep(sleepTime); catch (InterruptedException e) System.out.println(e);,public static void main(String args) Judge test1 = new Judge (1000, “test1“);test1.start();Judge test2 = new Judge (2000, “test2“);test2.start();System.out.println(test1.isAliv
5、e() ? “运行中“ : “已经终止“);System.out.println(test2.isAlive() ? “运行中“ : “已经终止“);try Thread.sleep(10000); catch (InterruptedException e) System.out.println(e);System.out.println(test1.isAlive() ? “运行中“ : “已经终止“);System.out.println(test2.isAlive() ? “运行中“ : “已经终止“); ,运行结果: 运行中 运行中 test1:Thu Jan 10 14:16:15
6、 CST 2013 test2:Thu Jan 10 14:16:15 CST 2013 test1:Thu Jan 10 14:16:16 CST 2013 test1:Thu Jan 10 14:16:17 CST 2013 test2:Thu Jan 10 14:16:17 CST 2013 test2:Thu Jan 10 14:16:19 CST 2013 已经终止 已经终止,9.2 线程优先级,Java给每个线程安排优先级以决定与其他线程比较时该如何对待该线程。线程的优先级是用来决定何时从一个运行的线程切换到另一个。叫做“上下文转换”(context switch)。决定上下文转换
7、发生的规则很简单: 线程可以自动放弃控制。在I/O未决定的情况下,睡眠或阻塞由明确的让步来完成。 线程可以被高优先级的线程抢占。在这种情况下,低优先级线程不主动放弃,处理器只是被先占无论它正在干什么处理器被高优先级的线程占据。,【实例9-2】Priority.java public class Priority public static void main(String args) Thread test1 = new MyThread1();Thread test2 = new Thread(new MyRunnable();test1.setPriority(10);test2.setP
8、riority(1);test2.start();test1.start(); class MyThread1 extends Thread public void run() for (int i = 0; i 10; i+) System.out.println(“线程 1 第“ + i + “次执行“);try Thread.sleep(1000); catch (InterruptedException e) e.printStackTrace(); ,class MyRunnable implements Runnable public void run() for (int i =
9、 0; i 10; i+) System.out.println(“线程 2 第“ + i + “次执行“);try Thread.sleep(1000); catch (InterruptedException e) e.printStackTrace(); 运行结果: 线程 2 第0次执行 线程 1 第0次执行 线程 2 第1次执行 线程 1 第1次执行 线程 1 第2次执行 线程 2 第2次执行 线程 2 第3次执行 线程 1 第3次执行 线程 1 第4次执行 线程 2 第4次执行 线程 2 第5次执行 线程 1 第5次执行 线程 1 第6次执行 线程 2 第6次执行 线程 1 第7次执
10、行 线程 2 第7次执行 线程 1 第8次执行 线程 2 第8次执行 线程 1 第9次执行 线程 2 第9次执行,9.3 多线程的实现,为了创建一个新的线程,我们需要做些什么?显然,必须指明这个线程所要执行的代码,而这就是在 Java 中实现多线程所需要做的。Java语言提供了两种线程的实现方式: 继承Thread类; 实现Runnable接口。,9.3.1 继承Thread类java.lang中定义了一个直接从根类Object中派生的Thread类,所有以这个类派生的子类或间接子类均为线程。在这种方式中,需要作为一个线程执行的类只能继承、扩充单一的父类。Tread类有两种构造方法: publ
11、ic Thread(): 用来创建一个线程对象; public Thread(Runnable r): 创建线程对象,参数r成为被创建的目标对象。这个目标必须实现Runnable接口,给出该接口的run()方法的方法体,在方法体中实现操作。,通过继承Thread类实现多线程的基本步骤为:1)定义Thread类的一个子类; 2)定义子类中的方法run(),覆盖父类中的方法run(); 3)创建该子类的一个线程对象; 4)通过start()方法启动线程对象。,9.3.2 实现Runnable接口实现Runnable接口是最常用的实现线程的方法,它打破了扩充Thread类方式的限制。如果有一个类,它
12、已继承了某个类,又想实现多线程,那就可以通过实现Runnable接口。其基本步骤如下: 1)定义一个实现Runnable接口的类; 2)定义方法run(); 3)创建该类的一个线程对象,并将该对象做参数,传递给Thread类的构造函数,从而生成Thread类的一个对象; 4)通过start()方法启动线程。,【实例9-3】ImpRunnable.javapublic class ImpRunnable implements Runnable int count = 1, number;public ImpRunnable (int num) number = num;System.out.pr
13、intln(“创建线程 “ + number);public void run() while (true) System.out.println(“线程 “ + number + “:计数 “ + count);if (+count = 6)return;public static void main(String args) for (int i = 0; i 5; i+)new Thread(new ImpRunnable (i + 1).start(); ,运行结果: 创建线程 1 创建线程 2 创建线程 3 创建线程 4 线程 1:计数 1 线程 1:计数 2 创建线程 5 线程 3
14、:计数 1 线程 3:计数 2 线程 3:计数 3 线程 3:计数 4 线程 1:计数 3 线程 3:计数 5 线程 1:计数 4 线程 1:计数 5 线程 4:计数 1 线程 4:计数 2 线程 4:计数 3 线程 5:计数 1 线程 4:计数 4 线程 5:计数 2 线程 5:计数 3 线程 5:计数 4 线程 5:计数 5 线程 4:计数 5 线程 2:计数 1 线程 2:计数 2 线程 2:计数 3 线程 2:计数 4 线程 2:计数 5,9.4 多线程的同步,当两个或两个以上的线程需要共享资源,它们需要某种方法来确定资源在某一刻仅被一个线程占用。达到此目的的过程叫做同步(synchr
15、onization)。 同步的关键是管程(也叫信号量semaphore)的概念。管程是一个互斥独占锁定的对象,或称互斥体(mutex)。在给定的时间,仅有一个线程可以获得管程。当一个线程需要锁定,它必须进入管程,所有其他的试图进入已经锁定的管程的线程必须挂起直到第一个线程退出管程,这些其他的线程被称为等待管程。一个拥有管程的线程如果愿意的话可以再次进入相同的管程。,Java语言中同步很简单,因为所有对象都有它们与之对应的隐式管程。进入某一对象的管程,就是调用被synchronized关键字修饰的方法。当一个线程在一个同步方法内部,所有试图调用该方法的同实例的其他线程必须等待。为了退出管程,并放
16、弃对对象的控制权给其他等待的线程,拥有管程的线程仅需从同步方法中返回。,9.5 综合实例,【实例9-5】使用多线程实现Java程序:在屏幕上显示时间,每隔一秒钟刷新一次。Time.javaimport java.awt.*; import java.applet.*; import java.util.Date; public class Time extends Applet implements Runnable Thread clockThread;Font font;public void init() font = new Font(“TimesRoman“, Font.BOLD,
17、64);public void start() if (clockThread = null) clockThread = new Thread(this, “Show time“);clockThread.start();,public void run() while (clockThread != null) repaint();try clockThread.sleep(1000); catch (InterruptedException e) public void paint(Graphics g) Date now = new Date();g.setFont(font);g.s
18、etColor(Color.BLACK);g.drawString(now.getHours() + “:“ + now.getMinutes() + “:“+ now.getSeconds(), 5, 50);public void stop() clockThread.stop(); ,运行结果:,9.6 习题,4. 下列说法中错误的一项是( ) A. 一个线程是一个Thread类的实例 B. 线程从传递给纯种的Runnable实例run()方法开始执行 C. 线程操作的数据来自Runnable实例 D. 新建的线程调用start()方法就能立即进入运行状态 5. 下列关于Thread类提
19、供的线程控制方法的说法中,错误的一项是( ) A. 在线程A中执行线程B的join()方法,则线程A等待直到B执行完成 B. 线程A通过调用interrupt()方法来中断其阻塞状态 C. 若线程A调用方法isAlive()返回值为true,则说明A正在执行中 D. currentThread()方法返回当前线程的引用,6. 在Java程序中,run()方法的实现有两种方式: 和 。 7. 处于新建状态的线程可以使用的控制方法是 和 。 8. sleep()和wait()有什么区别 ? 9. 同步和异步有何异同,在什么情况下分别使用他们?举例说明。 10. 启动一个线程是用run()还是start()?,