240 likes | 395 Views
厦门大学数据库实验室 JAVA 多线程编程演示. 报告人:谢荣东 导师:林子雨 2014 年 7 月 26 日. 进程和线程. 进程和线程都是一个控制流程。 一个进程通常对应于一个程序。 一个程序可以由多个不同的线程构成。. 进程. 程序:利用编程语言开发的一个工具软件, 静态的,在没有启动运行之前只是磁盘中的一个普通文件 进程 : 程序启动之后就变成了进程 动态 window 支持多进程,但 cpu 只有一个,所以同一时间只能运行一个进程 动态的 进程相互独立,不共享数据 线程:进程在运行过程中的执行走向,线索
E N D
厦门大学数据库实验室JAVA多线程编程演示 报告人:谢荣东 导师:林子雨 2014年7月26日
进程和线程 进程和线程都是一个控制流程。 一个进程通常对应于一个程序。 一个程序可以由多个不同的线程构成。
进程 程序:利用编程语言开发的一个工具软件, 静态的,在没有启动运行之前只是磁盘中的一个普通文件 进程:程序启动之后就变成了进程 动态 window 支持多进程,但cpu只有一个,所以同一时间只能运行一个进程 动态的 进程相互独立,不共享数据 线程:进程在运行过程中的执行走向,线索 单线程 : 如果只有一条单一线索 存在的问题 效率相对底下 没有充分合理的利用cpu 多线程 : 多于一条的执行走向线程 共享内存 可以实现多任务
进程 根据定义,进程为一个数据结构及能在其上进行的一次操作, • 它有两个基本特征, 1:进程是可用于资源的独立单位, 2:进程同时又是一个可独立调度和分派的基本单位, 这两个基本属性使之能够独立运行,也能够并发运行。但是在并发运行的时候,系统还需要执行一系列操作: 1、需要创建进程,并为之分配其所必需的资源。 2、撤销进程,对资源进行回收。 3、进程切换,它需要保留当前进程的CPU环境和设置新选中进程的CPU环境。 为此需要花费不少处理时间。正因为进程拥有资源,所以在并发执行进程的时候, 在创建、撤销和切换,系统需要付出较大的开销,因此,系统中设置的进程不能太多, 进程切换的频率也不能过高,这就限制了并发程度的提高。 为了解决这一问题,于是产生并引入了线程概念。
进程 进程:正在进行的程序 我们现在使用的操作系统都是多任务的,即能够 同时 执行多个应用程序。实际是操作系统负责对CPU等设备资源进行分配和管理,虽然这些设备某一时刻只能做一件事情,但以非常小的时间间隔交替执行多个程序,就给人同时执行多个程序的感觉。如从C盘复制文件到D盘的同时从E盘复制文件到F盘。
线程(threads) • 线程因为具有许多进程所具有的特征,因此被称为 轻量级 进程。计算机科学术语,指运行中程序的调度单位。 • 线程是进程中的实体,一个进程可以拥有多个线程,一个线程必须有一个父进程。 • 线程不拥有系统资源,只拥有运行必须的一些数据结构;它与父进程的其它线程共享该进程所拥有的全部资源。 • 线程可以创建和撤消,从而实现程序的并发执行。 • 一般,线程具有就绪、阻塞和运行三种基本状态。
线程 一个进程中可以包含一个或多个线程,一个线程就是程序内部的一条执行线索。 在单线程中,程序代码按调用顺序依次往下执行,不能实现两段程序代码同时交替运行的效果。如果一个程序中要实现两段程序代码同时交替运行,就需要产生多个线程,并指定每个线程上所要运行的程序代码段,这就是多线程。 程序启动运行时,就自动产生了一个线程,main方法就是在这个线程上运行的,当不再产生新的线程时,程序就是单线程。
多线程 多线程在实际工作场景的应用: QQ聊天(键盘的输入与等待)、 访问网页(应用服务器必须能响应多个用户请求) 创建多线程的方法有2种: 继承Thread类 实现Runnable接口
Thread Java的线程通过Thread类来控制,一个Thread类的对象代表一个线程,而且只能代表一个线程。 通过Thread类和它定义的对象,我们能获得当前线程对象、获取某一线程的名称、可以实现控制线程暂停一段时间等功能。 每个线程都是通过某个特定Thread对象所对应的方法run( )来完成其操作的,方法run( )称为线程体。 使用start()方法,线程进入Runnable(可运行)状态,它将向线程调度器注册这个线程。 调用start()方法并不一定马上会执行这个线程,正如上面所说,它只是进入Runnable 而不是Running。 注意,不要直接在程序中调用线程的run()方法。 见 线程图
线程状态 阻塞 Blocked 创建new dead 死亡 解除阻塞 阻塞事件 就绪 Runnable 运行 Running start() run() 运行完毕 线程调度
Thread 在单线程中,main方法必须等到run方法返回后才能继续往下执行。 而多线程中,调用start方法启动线程,运行run方法后,main方法可以不必等待run方法返回就继续运行,而另一个线程在一边独自运行,并不影响原来的main方法的运行。
Thread 1.要将一段代码放在一个新的线程上运行,该代码所在的类应该继承Thread 或 实现Runnable 接口,而需要执行的代码写在run方法里面。 2.启动一个线程,不是调用run方法,而是调用Thread对象的start方法。start方法将产生一个新的线程,并在该线程上运行该Thread对象中的run方法。运行的其实是我们的类(Thread子类)的run方法,多态。 3.run方法执行完后,线程也就结束了,所以,我们可以通过控制run方法中的循环条件来控制线程的终止。
继承Thread • public class TestThread { • public static void main(String[] args) { • MyThread myThread = new MyThread(); • myThread.setName("myThread");//设置线程的名称 • myThread.start();//启动线程 • for(int i=0;i<100;i++){ • //主线程 • System.out.println(Thread.currentThread().getName() + “***" + i); • } • } • } • class MyThread extends Thread { • //需要在线程中运行的代码写在run方法中 • public void run() { • for (int i = 0; i < 100; i++) { • //获得当前执行的线程的名称 • System.out.println(Thread.currentThread().getName() + "---" + i); • } • } • }
Runnable 通过实现Runnable接口并实现接口中定义的唯一抽象方法run(),可以创建一个线程。
实现Runnable接口 • public class TestRunnable{ • public static void main(String[] args){ • MyThread2 my = new MyThread2();//创建一个Runnable接口实现类 • Thread thread = new Thread(my);//把my传递给Thread类 • thread.setName("线程-1"); • thread.start(); • for(int i = 0;i<100;i++){ • System.out.println(Thread.currentThread().getName()+"***"+i); • } • } • } • class MyThread2 implements Runnable{ • //重写Runnable中的run抽象方法,写入需要执行的代码 • public void run(){ • for(int i=0;i<100;i++){ • System.out.println(Thread.currentThread().getName()+"--"+i); • } • } • }
线程的生命周期 • 与人有生老病死一样,线程也同样要经历 新建(new) 和其它的Java对象一样,只分配内存空间和初始化成员变量 就绪(runnable) 调用了start方法后进入就绪状态,什么时候运行取决线程调度器 运行(running) 获得了时间片开始执行run方法体 阻塞(blocked) 时间片用完,系统会剥夺其占有的资源,让其他线程有机会执行,优先级高的先执行 死亡(dead) run方法体执行完,正常结束或抛出Exception或Error 或 调用stop方法(不推荐) 五种状态。
分析两种实现多线程的方式: 写一个程序,模拟4个售票窗口共同卖100张火车票的程序。 1:使用继承Thread类方式实现。 2:使用实现Runnable接口方式实现。
继承Thread,每个线程卖了100张票 [没有共享数据] • public class TicketThread { • public static void main(String[] args){ • // 执行继承Thread的线程 • new MyThread().start(); • new MyThread().start(); • new MyThread().start(); • new MyThread().start(); • } • } • class MyThread extends Thread { • // 车票数量 • private int tickets = 100; • public void run() { • while (tickets > 0) { • System.out.println(this.getName()+"卖出第["+(tickets--)+"]张火车票"); } • } • }
实现Ruaable,线程总共卖100张票[有共享数据] • public class TicketRunnable { • public static void main(String[] args) { • //实现Runnable接口实现类 • myRunnable myR = new myRunnable(); • new Thread(myR).start(); • new Thread(myR).start(); • new Thread(myR).start(); • new Thread(myR).start(); • } • } • class myRunnable implements Runnable { • //火车票数量 • private int tickets = 100; • public void run() { • while (tickets > 0) { System.out.println(Thread.currentThread().getName()+"卖出第["+(tickets--) +"]张火车票."); • } • } • }
两种线程创建方式的比较 使用Runnable接口 实际工作中,几乎所有的多线程应用都用实现Runnable这种方式。 Runnable适合多个相同程序代码的线程去处理同一资源的情况。把虚拟CPU(线程)同程序的代码、数据有效的分离,较好的体现了面向对象的设计思想。 避免由于Java的单继承特性带来的局限性。也就是如果新建的类要继承其他类的话,因为JAVA中不支持多继承,就只能实现java.lang.Runnable接口。 有利于程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。 继承Thread类 不能再继承他类了。 编写简单,可以直接操纵线程,无需使用Thread.currentThread()。
总结 • 两种实现方式:Thread,Runnable • 线程状态:创建线程,就绪Runnable,运行Running,挂起Block,结束dead • 实现Runnable接口相对于继承Thread类的好处