多线程

来源:互联网 发布:淘宝seo搜索优化技巧 编辑:程序博客网 时间:2024/06/18 12:32

1.进程与线程

进程:程序的一次动态执行过程,它需要经历从代码加载、代码执行到执行完毕的一个完整过程。
线程:一个进程在执行过程中可以产生多个更小的程序单元,这些更小的单元称为线程。

大多数程序语言只能运行单独一个程序块,无法同时运行不同的多个程序块,而Java的多线程机制可以弥补这个缺憾,它可以让不同的程序块同时运行。
所谓的线程是指程序的运行流程,多线程机制则是指可以同时运行多个程序块。

2.线程的实现方式

实现多线程有两种手段:①继承Thread类;②实现Runnable接口。
(1)继承Thread类
在Thread子类中,必须明确地覆写Thread类中的run()方法,此方法为线程的主体。
格式:

class 类名称 extends Thread{属性…;方法…;public void run(){    线程主体;}}

注:如果要正确启动线程,要调用从Thread类中继承来的start()方法。而在线程启动时虽然调用的是start()方法,但实际上调用的却是run()方法定义的主体。

实现实例:

MyThread mt1=new MyThread(“线程A”);MyThread mt2=new MyThread(“线程B”);mt1.start();mt2.start();

在启动多线程时为什么必须通过start()方法启动,而不能直接调用run()方法呢?
因为线程的运行需要本机操作系统的支持。实际上在start()方法的定义中可以知道实际上此处真正调用的却是start0()方法,此方法在声明处使用了native关键字声明,此关键字表示调用本机的操作系统函数,因为多线程的实现需要依靠操作系统的支持。

如果一个类通过继承Thread类来实现,那么只能调用一次start()方法,如果调用多次则将会抛出”IllegalThreadStateException”异常。而且如果实现Thread类使用多线程,必定会受到单继承局限的影响。故推荐使用Runnable接口来实现多线程。

(2)实现Runnable接口(推荐使用)
Runnable接口中定义了一个抽象方法:public void fun();
格式:

class 类名称 implements Runnable{属性…;方法…;public void run(){    线程主体;}}

虽然没有Thread类中的start()方法来启动,但是仍然可依靠Thread类来完成启动,在Thread类中提供了public Thread(Runnable target)和public Thread(Runnable target,String name)两个构造方法,这两个构造方法都可以接收Runnable的子类实例对象,所以可以依靠此来启动多线程。

实现实例:

MyThread mt1=new MyThread(“线程A”);MyThread mt2=new MyThread(“线程B”);Thread t1=new Thread(mt1);Thread t2=new Thread(mt2);mt1.start();mt2.start();

从以上内容可以发现,无论使用哪种形式,最终都要依靠Thread类才能启动多线程。

(3)两者的区别与联系
Thread类的定义:public class Thread extends Object implements Runnable
从这儿可以看出Thread类也是Runable接口的子类,但是在Thread类中并没有完全地实现Runnable接口中的run()方法。
Thread和Runnable的子类都同时实现了Runnable接口,之后将Runnable的子类实例放到了Thread类之中。与代理模式的设计很类似。

实际上Thread类和Runnable接口之间在使用上也是有区别的,如果一个类继承Thread类,则不适合多个线程共享资源,而实现了Runnable接口,就可以方便地实现资源共享。

使用Runnable接口相对于继承Thread类来说,有如下优势:
①适合多个相同程序代码的线程去处理同一资源的情况。
②可以避免由于Java单继承特性带来的局限性。
③增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。

故在以后的开发中推荐使用Runnable接口实现多线程。

3.线程的状态

这里写图片描述
任何线程一般具有5种状态,即创建、就绪、运行、阻塞、终止。
①创建状态
在程序中用构造方法创建了一个线程对象后,新的线程对象便处于新建状态,此时它已经有了相应的内存空间和其他资源,但还处于不可运行状态。新建一个线程对象可采用Thread类的构造方法来实现,如:Thread a=new Thread();
②就绪状态
新建线程对象后,调用该线程的start()方法就可以启动线程。当线程启动时,线程进入就绪状态。此时,线程将进入线程队列排队,等待CPU服务,这表明它已经具备了运行条件。
③运行状态
当就绪状态的线程被调用并获得处理器资源时,线程就进入了运行状态。此时,自动调用该线程对象的run()方法。run()方法定义了该线程的操作和功能。
④堵塞状态
一个正在执行的线程在某些特殊情况下,如被人为挂起或需要执行耗时的输入/输出操作时,会让出CPU并暂时中止自己的执行,进入阻塞状态。在可执行状态下,如果调用sleep()、suspend()、wait()等方法,线程都将进入堵塞状态。堵塞时,线程不能进入排队队列,只有当引起堵塞的原因被消除后,线程才可以转入就绪状态。
⑤死亡状态
线程调用stop()方法或run()方法执行结束后,即处于死亡状态。处于死亡状态的线程不具有继续运行的能力。

4.线程相关的操作方法

虽然Thread类实现了Runnable接口,但是操作线程的主要方法并不在Runnable接口中,而是在Thread类中。
这里写图片描述

(1)取得和设置线程名称
在Thread类中可以通过getName()方法取得线程的名称,还可以通过setName()方法设置线程的名称。
线程的名称一般在启动线程前设置,但也允许已经运行的线程设置名称。允许两个线程对象有相同的名称,但应该尽量避免这种情况的发生。
注:如果没有名称,系统会为其自动分配名称,格式为Thread-Xx。
在Java中所有的线程都是同时启动的,哪个线程先抢到了CPU资源,哪个线程就先运行。

Java程序每次运行至少启动几个线程?
至少启动两个线程。一个是main线程,一个是垃圾收集线程。
(2)判断线程是否启动
通过Thread类的start()方法通知CPU这个线程已经准备好启动,然后就等待分配CPU资源,运行此线程。在Java中可以使用isAlive()方法来测试线程是否以及启动而且仍然在执行。
注:主线程有可能比其他线程先执行完。因为线程操作的不确定性,所有主线程有可能最先执行完,那么此时其他线程不会受到任何影响,并不会随着主线程的结束而结束。
(3)线程的强制执行
在线程操作中,可以使用join()方法让一个线程强制执行,线程强制运行期间,其他线程无法运行,必须等待此线程完成后才可以继续执行。
注:join()方法会抛出InterruptedException的异常,所以编写时必须把join()方法编写在try…catch块内。
(4)线程的休眠
在程序中允许一个线程进行暂时的休眠,直接使用Thread.sleep()方法即可实现休眠。
(5)中断线程
当一个线程运行时,另外一个线程可以直接通过interrupt()方法中断其运行状态。
(6)后台线程
在Java程序中,只要前台有一个线程在运行,则整个Java进程都不会消失,所有此时可以设置一个后台线程,这样即使Java进程结束了,此后台线程仍然会继续执行。要想实现这样的操作,直接使用setDaemon()方法即可。
(7)线程的优先级
在Java的线程操作中,所有的线程在运行前都会保持在就绪状态,那么此时,哪个线程的优先级高,哪个线程就有可能会先被执行。
这里写图片描述

在Java的线程中使用setPriority()方法可以设置一个线程的优先级,在Java中一共有3种优先级。

1.public static final int MIN_PRIORITY,最低优先级,表示的常量是1 2.public static final int NORM_PRIORITY,中等优先级(线程默认的优先级),表示的常量是5  3.public static final int MAX_PRIORITY,最高优先级,表示的常量是10. 

注:并非线程的优先级越高就一定会执行,哪个线程先执行将由CPU的调度决定。
主方法的优先级是NORM_PRIORITY.
(8)线程的礼让
在线程操作中,也可以使用yield()方法将一个线程的操作暂时让给其他线程执行。

5.同步与死锁

一个多线程的程序如果是通过Runable接口实现的,则意味着类中的属性将被多个线程共享,那么这样一来就会带来一种问题,如果这多个线程要操作同一资源时就有可能出现资源的同步问题。如:买票程序中,如果多个线程同时操作时就有可能出现卖出票为负数的问题。
(1)所谓同步就是指多个操作在同一个时间段内只能有一个线程进行,其他线程要等待此线程完成之后才可以继续执行。
解决资源共享的同步操作,可以使用同步代码块和同步方法两种方式完成。
①同步代码块
格式:

synchronized(同步对象){    需要同步的代码;}

注:在使用同步代码块是必须指定一个需要同步的对象,但一般都将当前对象(this)设置成同步对象。
②同步方法
格式:

synchronized 方法返回值 方法名称(参数列表){}

(2)同步保证了资源共享操作的正确性,但是过多同步也会产生死锁问题。
所谓死锁就是指两个线程都在等待对方先完成,造成了程序的停滞,一般程序的死锁都是在程序运行时出现的。
注:多个线程共享资源时需要进行同步,以保证资源操作的完整性,但是过多的同步就有可能产生死锁。

6.Object类对线程的支持

Object类是所有类的父类。在此类中有一下几种方法是对线程的操作有所支持的。

一般来说,所有等待的线程会按照顺序进行排列,如果现在使用了notify()方法,则会唤醒第1个等待的线程执行,而如果使用了notifyAll()方法则会唤醒所有的等待线程,哪个线程的优先级越高,哪个线程就有可能先执行。

7.线程的生命周期

这里写图片描述
3个新方法:

suspend()方法:暂时挂起线程;resume()方法:恢复挂起的线程;stop()方法:停止线程。

虽然有如上3种方法,但是对于线程中却并不推荐使用,因为这3种方法在操作时可能产生死锁问题。
如何停止一个线程的执行?在多线程的开发中可以通过设置标志位的方式停止一个线程的运行。

0 0
原创粉丝点击