线程与线程实现 主讲:赖国荣 QQ:31343080 www.sms98.cn
目标 线程概念与原理 线程实现 线程的生命周期 线程同步: Thread类和Runnable接口 多线程应用—Runnable接口/Thread类 线程的优先级 线程中常用方法 线程的生命周期 线程同步: 线程同步的目的/线程同步的具体应用
概 念 进程:程序的一次执行过程,实质上就是一个执行的程序。 线程:是一个程序中彼此独立、能够并发执行的部分(代码片段/程序流)。 进程资源占用大,进程间通信困难。 线程资源占用小,线程间通信容易。
线程的特点 线程也称为执行内容,是可以同时运行的独立过程。 线程有开始、中间和结束部分,即有一定的生命周期。 同一个类的多个线程是共享一块内存空间和一组系统资源。 线程不能作为具体的可执行命令体存在。最终用户不能直接执行线程,线程只能运行在程序中。
创建线程 两种方法来创建线程: 继承java.lang.Thread类,并覆盖run( )方法。 class mythread extends Thread { public void run( ) { /* 覆盖该方法*/ } 实现java.lang.Runnable接口,并实现run( )方法。 class mythread implements Runnable{ /* 实现该方法*/
Thread类提供的构造函数 public Thread (); public Thread (String name); public Thread (Runnable target); public Thread ( Runnable target,String name); public Thread (ThreadGroup group, Runnable target); public Thread (ThreadGroup group,String name);
继承Thread类例子 public class SimpleThread extends Thread { public SimpleThread( String ms ){ super( ms ); } public void run( ){ for( int i=1;i<=10;i++ ){ System.out.println( i+" "+getName( ) ); try{ Thread.sleep(2*1000); }catch(InterruptedException e){ e.printStackTrace(); System.out.println( i+" end! "); 让当前线程休眠2秒种
实现Runnable接口 public class SimpleThread implements runnable { public void run( ){ for( int i=1;i<=10;i++ ){ System.out.println( i+" "+getName( ) ); try{ Thread.sleep(2*1000); }catch(InterruptedException e){ e.printStackTrace(); } System.out.println( i+" end! ");
线程的启动 新建的线程不会自动开始运行,必须通过start( )方法启动线程。如果不调用这个方法,线程将不会运行。 也就是说可以事先创建线程,并在需要的时候才启动他们。如: 继承Thread的线程: Mythread t = new Mythread ( ); t.start( ); 实现Runnable接口的线程: Runnable mt = new Mythread ( ); Thread t = new Thread(mt);
线程执行过程 run()方法能够调用其他方法,引用其他的类,申明变量。 run()方法在程序中确定另一个并发线程的执行入口。 调用start( )方法时,将创建一个新的线程,接着它将调用 run( ) 方法。 run( ) 方法中的代码定义执行线程所需的功能。 run()方法能够调用其他方法,引用其他的类,申明变量。 run()方法在程序中确定另一个并发线程的执行入口。 当run()方法中的任务完成返回时,该线程也将结束。
继承Thread类多线程例子 思考: 1、运行结果是怎样的? 2、每次运行结果是否都一样?
实现Runnable接口多线程例子 注意:通过实现Runnable接口来创建一个线程类,启动的时候必须将该类的对象作为参数放到一个实例化的Thread类对象中去.
使用runnable接口创建多线程的优点: 1.对象可以自由地继承自另一个类;解决了java只支持单继承的特点。 2.同一个runnable对象可以传递给多个线程,可以实现资源共享。 3.减小创建新线程实例所需的可观内存和cpu时间。
一个向下移动的飞机案例 目标:用一个线程来控制飞机移动的全过程,且飞机是沿着Y坐标向下移动的
线程优先级 Java 中的线程优先级是在 Thread 类中定义的常量 有关优先级的方法有两个: Thread.MIN_PRIORITY : 值为 1 Thread.NORM_PRIORITY : 值为 5(缺省值) Thread.MAX_PRIORITY : 值为 10 有关优先级的方法有两个: final void setPriority(int level) : 修改线程的当前优先级 final int getPriority() : 返回线程的优先级
线程优先级应用 思考: 通过分析该例子,你认为它的执行结果是什么? 思考:给每个线程设置了自己的优先级别后执行结果会有什么变化?
Thread中提供的方法2-1 方法 用途 final String getName( ) 返回线程的名称 final void setName(String name) 将线程的名称设置为由name指定的名称 static Thread currentThread() 得到当前线程 void start( ) 调用run( )方法启动线程,开始线程的执行 static void sleep( ) 用于将线程挂起一段时间 void interrupt() 中断/唤醒一个线程
Thread中提供的方法2-2 方法 用途 final boolean isDaemon( ) 检查线程是否为守候线程 final void setDaemon(boolean) 根据传入的参数,将线程标记为守候线程(true)或用户线程(false) final void join( ) throws InterruptedException 等待线程结束 static void yield() 使正在执行的线程临时暂停,并允许其他线程执行 static int activeCount( ) 返回激活的线程数 final boolean isAlive() 判断线程是否仍在运行,是返回true,否则返回 false
休眠线程 sleep(): 让线程停止执行一段时间,该时间由给定的毫秒数决定。在线程休眠过程中,线程通常不能唤醒他们自己。 1. public static void sleep(long millis) throws InterruptedException 2. public static void sleep(long millis, int nanos) throws InterruptedException
唤醒线程 interrupt() :唤醒线程,一个线程向一个指定的线程发出一个中断信号。 如果目标线程是阻塞在与线程相关的方法(sleep( ),join( ),wait( ))中,则目标线程会转到一个非阻塞状态,如果该线程被中断,则给出一个interruptException异常,否则会设置一个布尔值标明该线程已经被中断过。 方法: public void interrupt()
休眠-唤醒线程例子 让线程暂停 5分钟 唤醒/中断该线程
让步线程 yield():有时候线程可能正在等待事情的发生,或者可能正在进入这样一段代码:把cpu时间释放出来给另一个线程将可能改善系统性能或者用户操作。这样线程把cpu让给别的线程,而不用进入休眠状态而等待很长时间。 方法:public static void yield() 方法为静态方法,只能影响当前正在运行的线程。 该方法只是暗示,没有任何机制保证它将会被采纳。
1、final boolean isAlive() 返回true,线程执行完毕。 等待线程 判断一个线程是否执行完毕的方法: 1、final boolean isAlive() 返回true,线程执行完毕。 2、final void join()throws InterruptedException 让当前线程等待目标线程执行完毕。 例子:JoinThread.java
线 程 状 态 一个线程可以处于以下四个状态之一: 新建(new):线程对象已经建立,但还没有启动,所以他不能运行。 就绪(runnable):这种状态下,只要调度程序把时间片分配给线程就可以执行,也就是说任何时刻它可以执行也可以不执行,不同于死亡和阻塞状态。 死亡(dead):线程死亡通常是从run方法返回的,在Java2放弃stop前也可以通过该方法实现,但容易让程序进入不稳定状态。 阻塞(blocked):线程能够运行,但有某个条件阻止他的运行。处于阻塞状态的线程调度机制将忽略线程,直到线程重新进入就绪状态,它才有可能执行操作。
线程四大状态转换 sleep( ) 睡眠, yield( ) 让步成功, suspend( )挂起 wait( )等待 I/O出现阻塞 Interrupt( ) 唤醒 睡眠结束
想像下,几个人围着桌子吃蛋糕,当你举起叉子,准备叉起最后一块蛋糕,正当叉子碰到蛋糕的时候才发现它已经被另一个叉子叉起。 共享资源的冲突 例子: 想像下,几个人围着桌子吃蛋糕,当你举起叉子,准备叉起最后一块蛋糕,正当叉子碰到蛋糕的时候才发现它已经被另一个叉子叉起。 这类现象就是共享资源(蛋糕)中出现的冲突,而两个人同时用叉子叉同一块蛋糕的过程我们就称为并发。 应用实例 模拟一个卖票系统:现有100张票,共分四个窗口售票 例子:TicketThreadDemo.java
并发:由两个及其以上的线程同时访问一个共享资源的现象称为并发。 并发和互斥锁 并发:由两个及其以上的线程同时访问一个共享资源的现象称为并发。 互斥锁:一种标志,表明在给定的时刻只允许一个线程访问共享资源,具有互相排斥的效果;这种机制称为“互斥”(mutex) 有没有办法解决这样的事情?避免它发生?
同步Synchronized 同步:当两个或两个以上的线程需要共享资源,必须使用某种方法来确定资源在某一时刻仅被一个线程占用,达到此目的的过程叫做同步。 在JAVA中通过互斥锁标志Synchronized关键字的运用来实现方法/变量的同步。
方法级同步(method-level synchronized) 同步的实现 Java中同步的两种方法: 方法级同步(method-level synchronized) synchronized void method( ) { //同步的方法 } 程序块级同步( block-level synchronized ) synchronized ( object ) { //要同步的语句 需要互斥使用的对象
在该类中实例化一个Callme类的对象,并调用call()方法让其输出指定的字符串, 非同步方法例子 在该类中实例化一个Callme类的对象,并调用call()方法让其输出指定的字符串,
运行效果比较 设计本意 实际结果:
方法级同步 1、实现方法:在要标志为同步的方法前加上synchronized关键字。如: public synchronized void call(String msg){ } 2、实现原理:当调用对象的同步方法时,线程取得对象锁(lock)或监视器;如果另一个线程试图执行任何同步方法时,他就会发现他被锁住了,进入挂起状态,直到对象监视器上的锁被释放时为止。当锁住方法的线程从方法中返回时,只有一个排队等候的线程可以访问对象。 3、锁的作用域:该方法被执行的整个时间。
代码块级同步-1 1、临界区:需要进行互斥的代码段,而非整个方法 2、实现方法:用synchronized来指定某个对象,此对象的锁被用来对花括号内的代码进行同步控制。如: synchronized( target ){ target.call(msg); } 3、实现原理:在进入同步代码前,必须得到object对象的锁,如果其他线程已经得到这个锁,那么就得等到锁被释放后才能进入临界区。 4、锁的作用域:只在代码块运行的时间内。
两种方法的比较 方法级同步: 代码块同步: public synchronized void call( String m){ System.out.print(" [ " +msg); try{ Thread.sleep(1000); }catch(InterruptedExceptione){ System.out.println(“exception”); } System.out.println(" ]"); 代码块同步: public void run(){ synchronized( target ){ target.call(msg); }
实 践 --雷电游戏 本游戏属于射击游戏,友机和敌机相对飞行;在控制下,友机可以左右移动,并且发射子弹攻打敌机,当发出的子弹打中敌机后,敌机爆炸,则本游戏结束;当友机和敌机相撞时,二架飞机同时都爆炸,本游戏结束,具体要求如下: 敌机从上向下运动,运动路线是随机的。 人控飞机可以用键盘控制飞机的方向和发射子弹,键盘左键为飞机向左移动,右键为飞机向右移动,上键为飞机发子弹。 子弹打中敌机时,产生爆炸效果,游戏结束。 敌机和人控飞机相撞时,二架飞机都爆炸,游戏结束。 例子:Fight.java
总 结 线程概念与原理 线程实现 线程的生命周期 线程同步: Thread类和Runnable接口 总 结 线程概念与原理 线程实现 Thread类和Runnable接口 通过Thread类和Runnable接口实现线程调度 多线程应用—Runnable接口/Thread类 线程的优先级 线程中常用方法 线程的生命周期 线程同步: 线程同步的目的/线程同步的具体应用