------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
学习多线程之前,需对以下几个概念有所认知:
进程:进程是动态的、是一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。
线程:线程依附于进程,可以理解为进程下的一个子执行路径,但没有进程线程无法单独执行。
两者间的区别:进程是重量级的计算机任务,需要给它分配独立的地址空间和系统资源等。不同进程的内部数据和状态都是完全独立,所以不同进程之间的通信或转换是需要承受负担的;而线程则属于轻量级的任务,不同线程执行过程中所需的系统资源则由进程进行统一分配,所以同一进程的不同线程执行过程中是会相互影响的,而且线程本身的数据通常放于寄存器,或者是程序执行时所使用的堆栈中,所以线程切换的负担要比进程切换小的多。
单线程:如果某个程序执行过程中,进程下只有唯一的一条线程,那么这便是单线程。
多线程:如果某个程序执行过程中,进程下有不同的线程在执行各自的任务,那么这就是多线程。
了解了以上概念,那么第一个问题就出现了。计算机为什么要使用多线程来执行任务呢?答案很简单,因为多线程单位时间内执行的任务量比单线程大,而且对计算机CPU而言,单线程无疑会造成CPU的极度浪费。
那么多线程在Java中是如何体现的?
1、同其他大多数编程语言不同,Java内置支持多线程编程(Multithreaded Programming)。
2、多线程程序中包含两条或两条以上并发运行的部分,程序中每个这样的部分都叫做一个线程。每个线程都有独立的执行路径,因此多线程是多任务处理的一种特殊形式。
多任务处理被所有的现代操作系统所支持。但是,多任务处理却有两种截然不同的类型:基于进程的和基于线程的。
基于进程:进程本质上是一个执行的程序。因此基于进程的多任务处理的特点是允许你的计算机同时运行两个或更多的程序。所以在基于进程的多任务处理中,程序是调度程序所分派的最小代码单位。例如:我们可以一边听音乐,一边在QQ上聊天。
基于线程:在该多任务处理环境中,线程是最小的执行单位。这意味着一个程序具有同时执行两个或者多个任务的功能。例如:我们打开一个QQ软件,可以同时和多个人聊天。
线程的一些状态以及不同状态间的转换方式:
Java中的线程大体可分为五种状态:
1、新建:这种情况指的是,通过New关键字创建了Thread类(或其子类)的对象
2、就绪:当一个线程创建了以后,其他的线程调用了它的start()方法,该线程就进入了就绪状态。处于这个状态的线程位于可运行池中,等待获得CPU的使用权。
3、运行:这时的线程指的是获得CPU的就绪线程,运行状态是所有线程都希望获得的状态。
4、死亡:处于运行状态的线程,在执行完run()方法之后,就变成了DEAD状态了。
5、阻塞:这种状态指的是处于运行状态的线程,出于某种原因,比如调用了sleep()方法、等待用户输入等而让出当前的CPU给其他的线程。
不同状态间的转换:
一、创建并运行线程
当调用start()方法后,线程开始执行run()方法中的代码。线程进入运行状态。可以通过Thread类的isAlive()方法来判断线程是否处于运行状态。当线程处于运行状态时,isAlive()返回true,当isAlive()返回false时,可能线程处于等待状态,也可能处于停止状态。
二、挂起和唤醒线程
一但线程开始执行run()方法,就会一直到这个run()方法执行完成这个线程才退出。但在线程执行的过程中,可以通过两个方法使线程暂时停止执行。这两个方法是suspend()和sleep()。在使用suspend()挂起线程后,可以通过resume()方法唤醒线程。而使用sleep()使线程休眠后,只能在设定的时间后使线程处于就绪状态(在线程休眠结束后,线程不一定会马上执行,只是进入了就绪状态,等待着系统进行调度)。虽然suspend()和resume()可以很方便地使线程挂起和唤醒,但由于使用这两个方法可能会造成一些不可预料的事情发生,因此,这两个方法被标识为deprecated标记,这表明在以后的jdk版本中这两个方法可能被删除,所以尽量不要使用这两个方法来操作线程。
三、终止线程的三种方法:
1、使用退出标志,使线程正常退出,也就是当run()方法完成后线程终止。
2、使用stop()方法强行终止线程(这个方法不推荐使用,因为stop()和suspend()、resume()一样,也可能发生不可预料的结果)。
3、使用interrupt()方法中断线程
Java中线程的创建:
一、继承Thread类
步骤:1、定义类继承Thread 2、复写Thread类的run()方法 3、创建新定义类的对象 4、调用start()方法启动线程
代码实现:
1 package come.file; 2 3 //1、定义TestThread类继承Thread类 4 class TestThread extends Thread{ 5 //2、复写Thread类的run()方法 6 public void run(){ 7 System.out.println("这是TestThread类"); 8 } 9 } 10 public class MyThread{ 11 public static void main(String[] args){ 12 //创建新定义的TestThread类的对象 13 TestThread t = new TestThread(); 14 //调用start()方法 15 t.start(); 16 } 17 }
二、实现Runnable接口
步骤:1、创建类实现Runnable接口 2、复写run()方法 3、调用new Thread(runnable)方式创建线程 4、调用start()方法启动线程
代码实现:
1 package come.file; 2 3 //1、创建类实现Runnable接口 4 class TestThread2 implements Runnable { 5 //2、复写run()方法 6 public void run(){ 7 System.out.println("这是TestThread2类"); 8 } 9 } 10 public class MyThread2 { 11 public static void main(String[] args) { 12 //3、调用new Thread(runnable)方式创建线程 13 Thread t2 = new Thread(new TestThread2()); 14 //4、调用start()方法启动线程 15 t2.start(); 16 } 17 }
注意:
1、在继承Thread的方式中,可以使用getName()方法,来获得当前线程的名字,这是因为在Thread类中,有这个方法。可是在实现Runnable方式中,却不可以使用this.getName(),因为Runnable接口没有这个方法(可以看出来,因为我们没有提示我们需要重写这个方法),所以只能通过Thread的静态方法Thread.currentThread()取得当前的Thread对象,在调用getName()方法,来取得当前线程的名字。
2、对Java来说,run()方法没有任何特别之处。像main()方法一样,它只是新线程知道调用的方法名称。因此,在Runnable上或者Thread上调用run方法是合法的。但并不启动新的线程。只有调用start()方法才会启动新线程。
Final:比较总结
通过对比两种方式,我们可以发现Runnable接口方式要优于继承Thread类方式。因为前者实现了接口后还可以继承其他的类,而且在这种情况下,多个线程来共享一个Runnable target对象,非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好的体现了面向对象的思想。
原文:http://www.cnblogs.com/shadowW-W/p/4644218.html