1,进程与线程
对于WORD来讲,每次启动一个WORD相当于操作系统上分配了一个进程。
线程实际上是进程的进一步划分,从WORD来看,可以把拼写检查当作一个线程处理,当然,会同时存在多个线程。
如果进程没有了,线程肯定消失了;但是线程消失,进程未必消失。所有线程都是在进程的基础之上并发(同时运行)。
现在如果同时运行多个任务,则所有的线程资源是共享的,被所有线程所公用。但是程序处理需要CPU,在同一个时间段会有多个程序执行,但是同一个时间点只能存在
一个程序运行,也就是说,所有程序都要抢占CPU资源。
2,JAVA多线程实现
在Java中实现多线程可以采用以下两种方式:
1)继承Thread类。
2)实现Runnable接口。
2.1 Thread类
Thread类是在java.lang包中定义的,java.lang包会在程序运行时候自动导入,无需手动import导入。
一个类继承了Thread类之后,那么此类具有了多线程的操作功能。
在Thread类的子类中,必须明确覆写run()方法,此方法为线程的主体。
一个线程子类的实现如下:
class MyThread extends Thread{ // 继承Thread类,作为线程的实现类 private String name ; // 表示线程的名称 public MyThread(String name){ this.name = name ; // 通过构造方法配置name属性 } public void run(){ // 覆写run()方法,作为线程 的操作主体 for(int i=0;i<10;i++){ System.out.println(name + "运行,i = " + i) ; } }};public class ThreadDemo01{ public static void main(String args[]){ MyThread mt1 = new MyThread("线程1 ") ; // 实例化对象 MyThread mt2 = new MyThread("线程2 ") ; // 实例化对象 mt1.run() ; // 调用线程主体 mt2.run() ; // 调用线程主体 }};
运行结果:
线程1运行,i=0线程1运行,i=1线程1运行,i=2线程1运行,i=3线程1运行,i=4线程1运行,i=5线程1运行,i=6线程1运行,i=7线程1运行,i=8线程1运行,i=9线程2运行,i=0线程2运行,i=1线程2运行,i=2线程2运行,i=3线程2运行,i=4线程2运行,i=5线程2运行,i=6线程2运行,i=7线程2运行,i=8线程2运行,i=9
以上程序是先执行完A,后执行B,并没有达到所谓的并发执行效果。
因为以上程序还是按照古老的形式调用的,通过:对象.方法。但是如果要想启动一个线程,必须使用Thead类中定义的start()方法。
一旦调用start()方法,实际上最终调用的是run()方法。修改如下:
class MyThread extends Thread{ // 继承Thread类,作为线程的实现类 private String name ; // 表示线程的名称 public MyThread(String name){ this.name = name ; // 通过构造方法配置name属性 } public void run(){ // 覆写run()方法,作为线程 的操作主体 for(int i=0;i<10;i++){ System.out.println(name + "运行,i = " + i) ; } }};public class ThreadDemo02{ public static void main(String args[]){ MyThread mt1 = new MyThread("线程A ") ; // 实例化对象 MyThread mt2 = new MyThread("线程B ") ; // 实例化对象 mt1.start() ; // 调用线程主体 mt2.start() ; // 调用线程主体 }};
运行结果:
线程A 运行,i = 0线程B 运行,i = 0线程A 运行,i = 1线程B 运行,i = 1线程A 运行,i = 2线程B 运行,i = 2线程A 运行,i = 3线程A 运行,i = 4线程A 运行,i = 5线程A 运行,i = 6线程A 运行,i = 7线程A 运行,i = 8线程A 运行,i = 9线程B 运行,i = 3线程B 运行,i = 4线程B 运行,i = 5线程B 运行,i = 6线程B 运行,i = 7线程B 运行,i = 8线程B 运行,i = 9
从以上效果来看,确实是并发执行的,哪个线程先抢占CPU资源,那个线程就执行。
注意:
一个线程只能启动一次,启动多次就会出错。如下:
package Thread1;class MyThread extends Thread{ // 继承Thread类,作为线程的实现类 private String name ; // 表示线程的名称 public MyThread(String name){ this.name = name ; // 通过构造方法配置name属性 } public void run(){ // 覆写run()方法,作为线程 的操作主体 for(int i=0;i<10;i++){ System.out.println(name + "运行,i = " + i) ; } }};public class demo1{ public static void main(String args[]){ MyThread mt1 = new MyThread("线程A ") ; // 实例化对象 MyThread mt2 = new MyThread("线程B ") ; // 实例化对象 mt1.start() ; // 调用线程主体 mt1.start() ; // 错误 }};
运行结果:
线程A 运行,i = 0线程A 运行,i = 1线程A 运行,i = 2线程A 运行,i = 3Exception in thread "main" 线程A 运行,i = 4线程A 运行,i = 5线程A 运行,i = 6线程A 运行,i = 7线程A 运行,i = 8线程A 运行,i = 9java.lang.IllegalThreadStateException at java.lang.Thread.start(Unknown Source) at Thread1.demo1.main(demo1.java:18)
3. Runnable接口实现多线程
通过Runnable接口的方式实现多线程,Runnable接口只定义了一个抽象方法。
private void run();
通过Runnable接口实现多线程:
class 类名称 implements Runnable{ 属性...; 方法....; public void run(){ } }
如果要想启动线程,则肯定依靠Thread类,但是如果之前直接继承了Thread类,则可以将start()方法直接继承下来使用,但是在Runnable接口中,
并没有start()方法,启动多线程一定要使用start()方法。
Thread类的构造:
public Thread ( Runnable target)
就利用以上构造方法,启动多线程。
RunnableThread mt = new RunnableThread("线程B ") ; // 实例化对象 Thread t1 = new Thread(mt) ; // 实例化Thread类对象 t1.start() ; // 启动多线程
例子如下:
class MyThread implements Runnable{ // 实现Runnable接口,作为线程的实现类 private String name ; // 表示线程的名称 public MyThread(String name){ this.name = name ; // 通过构造方法配置name属性 } public void run(){ // 覆写run()方法,作为线程 的操作主体 for(int i=0;i<10;i++){ System.out.println(name + "运行,i = " + i) ; } }};public class RunnableDemo01{ public static void main(String args[]){ MyThread mt1 = new MyThread("线程A ") ; // 实例化对象 MyThread mt2 = new MyThread("线程B ") ; // 实例化对象 Thread t1 = new Thread(mt1) ; // 实例化Thread类对象 Thread t2 = new Thread(mt2) ; // 实例化Thread类对象 t1.start() ; // 启动多线程 t2.start() ; // 启动多线程 }};
运行结果:
线程A 运行,i = 0线程B 运行,i = 0线程A 运行,i = 1线程B 运行,i = 1线程A 运行,i = 2线程B 运行,i = 2线程A 运行,i = 3线程A 运行,i = 4线程B 运行,i = 3线程B 运行,i = 4线程A 运行,i = 5线程B 运行,i = 5线程A 运行,i = 6线程B 运行,i = 6线程A 运行,i = 7线程B 运行,i = 7线程A 运行,i = 8线程B 运行,i = 8线程A 运行,i = 9线程B 运行,i = 9
从运行结果可以看出,已经完成多线程功能。
4.Thread类与Runnable接口
4.1Thread类与Runnable接口的联系
Thread类定义:
public class ThreadExtends Objectimplements Runnable
从定义格式可以发现,Thread类也是Runnable接口子类。
从类的关系上看,之前的做法非常类似代理设计模式!Thread类完成比主体线程更多的操作,例如:分配CPU资源,判断是否已经启动等
4.2 Thread类与Runnable接口的区别
使用Thread类,在操作多线程的时候无法达到资源共享的目的,而使用Runnable接口实现的多线程操作可以实现资源共享。
使用Thread类的操作:
package Thread1;class MyThread extends Thread{ // 继承Thread类,作为线程的实现类 private int ticket = 5 ; // 表示一共有5张票 public void run(){ // 覆写run()方法,作为线程 的操作主体 for(int i=0;i<100;i++){ if(this.ticket>0){ System.out.println("卖票:ticket = " + ticket--) ; } } }};public class demo1{ public static void main(String args[]){ MyThread mt1 = new MyThread() ; // 实例化对象 MyThread mt2 = new MyThread() ; // 实例化对象 MyThread mt3 = new MyThread() ; // 实例化对象 mt1.run() ; // 调用线程主体 mt2.run() ; // 调用线程主体 mt3.run() ; // 调用线程主体 }};
运行结果:
卖票:ticket = 5卖票:ticket = 4卖票:ticket = 3卖票:ticket = 2卖票:ticket = 1卖票:ticket = 5卖票:ticket = 4卖票:ticket = 3卖票:ticket = 2卖票:ticket = 1卖票:ticket = 5卖票:ticket = 4卖票:ticket = 3卖票:ticket = 2卖票:ticket = 1
发现一个卖出了15张票,也就是三个线程各自卖各自的5张票,也就是说现在没有达到资源共享的目的。
因为在每一个MyThread对象中都包含各自的ticket属性。
如果现在使用Runnable接口呢?同样启动多个线程,那么,所有的线程将卖出共同的五张票。
package Thread1;class MyThread implements Runnable{ // 继承Thread类,作为线程的实现类 private int ticket = 5 ; // 表示一共有5张票 public void run(){ // 覆写run()方法,作为线程 的操作主体 for(int i=0;i<100;i++){ if(this.ticket>0){ System.out.println("卖票:ticket = " + ticket--) ; } } }};public class demo1{ public static void main(String args[]){ MyThread mt = new MyThread() ; // 实例化对象 Thread t1=new Thread(mt) ; // 调用线程主体 Thread t2=new Thread(mt) ; // 调用线程主体 Thread t3=new Thread(mt) ; // 调用线程主体 t1.run(); t2.run(); t3.run(); }};
结果:
卖票:ticket = 5卖票:ticket = 4卖票:ticket = 3卖票:ticket = 2卖票:ticket = 1
可见,虽然现在启动了三个线程,但是三个线程一共才卖出了五张票,所以达到了资源共享的目的。
这是因为三个Thread都是调用了同一个Runnable子类的对象MyThread 。
4.3 Thread类与Runnable接口比较的结论。
实现Runnable接口比继承Thread类有如下的优点:
1)适合多个相同程序代码的线程去处理同一个资源(资源共享)
2)可以避免由于单继承局限所带来的影响。
3)增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的(共享)。
综合来看,开发中Runnable接口最合适。
在以后的章节中,使用多线程时候,都将以Runnable接口的实现作为操作的重点。