1.1进程与线程
1.2Java的多线程实现
在java中要想实现多线程可以用以下两种方式
继承Thread类
实现Runnable接口
1.2.1Thread类
java.lang包会在程序运行时自动导入,所以不用手动编写import语句。
一个类继承了Thread类之后,那么此类就具有了多线程的操作功能。
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) ; } } };
以上已经完成了一个线程的操作类,直接使用此类就可以完成功能。
以上的程序是先执行完A之后再执行B线程,并没有达到所谓的并发执行的效果。
因为以上的程序实际上还是按古老的形式,通过对象方法去调用,但是如果想要启动一个线程必须使用Thread类中定义的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() ; // 调用线程主体 } };
从此处的效果来看,确实是并发执行的,那个线程先抢到了CPU资源,那个线程就执行。
问题:
为什么不直接调用run()方法,而是通过strat()调用呢?
如果要解决这样的难题,则要打开Thread类的定义,在JDK的src.zip中全部都是JAVA的原程序代码,直接找到java.lang.Thread类,就可以打开Thread类的定义。
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 ThreadDemo03{ public static void main(String args[]){ MyThread mt1 = new MyThread("线程A ") ; // 实例化对象 mt1.start() ; // 调用线程主体 mt1.start() ; // 错误 } };
1.2.2Runnable接口
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) ;
}
}
};
如果要想启动线程则肯定依靠Thread类,但是之前如果直接继承了Thread类,则可以将start()方法直接继承下来并使用,但是Runnable接口中并没有此方法。
Thread类的构造:
public Thread(Runnable target)
就利用以上的构造方法,启动多线程。
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 t2 = new Thread(mt2); // 实例化Thread类对象 t1.start(); t2.start(); } };
从运行效果可以发现,已经完成了多线程的功能。
1.3Thread类与Runnable接口
1.3.1Thread类与Runnable接口的联系
Thread定义:
public class Thread
extends Object
implements Runnable
从定义格式上可以发现,Thread类也是Runnable接口的子类。
从类的关系上来看,之前的做法非常类似于代理设计模式,Thread类完成比线程主体更多的操作。例如:分配CPU资源,判断是否已经启动等。
1.3.2Thread类与Runnable接口的区别
使用Thread类在操作多线程的时候无法达到资源共享的目的,而使用Runnable接口实现的多线程操作可以实现资源共享。
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 ThreadDemo04{ public static void main(String args[]){ MyThread mt1 = new MyThread() ; // 实例化对象 MyThread mt2 = new MyThread() ; // 实例化对象 MyThread mt3 = new MyThread() ; // 实例化对象 mt1.run() ; // 调用线程主体 mt2.run() ; // 调用线程主体 mt3.run() ; // 调用线程主体 } };
发现一共卖了15张票,证明了三个线程各自卖各自的5张票,也就是说现在并没有达到资源共享的目的。
因为在每一个MyThread对象中都包含各自的ticket属性。
如果现在使用Runnable接口呢?同样启动多个线程,那么所有的线程将卖出共同的5张票。
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 RunnableDemo02{ public static void main(String args[]){ MyThread mt = new MyThread() ; // 实例化对象 new Thread(mt).run() ; // 调用线程主体 new Thread(mt).run() ; // 调用线程主体 new Thread(mt).run() ; // 调用线程主体 } };
从运行效果来看,现在启动了三个线程,但三个线程一共才卖了5张票。所以达到了资源共享的目的。
1.3.3Thread类与Runnable接口的使用结论
1.4线程的状态
实际上,线程调用start()方法的时候不是立刻启动的,而是要等待CPU进行调度。
2、总结