申明:线程的概念以及进程的相关概念,可以参考网络上其他资料,这里只讨论多线程是怎么实现。
一、多线程的简单理解
明白什么是多线程,小生通俗一点的理解为:在一个程序里,我想同时让这个程序完成多个任务。
比如:让主函数 main 在打印1~100之间的所有整数的时候,要求在主函数打印到 20 的时候,再运行另一个类里的程序,让它打印10~100之间的所有整数。
这里忽略同进程内的多线程之间的抢占时间问题,上面的举例需求是要求一个程序只要有发生同时运行俩个程序的情况就行,即不准出现无论程序跑多少次都是先把 main 函数的1~100 的所有整数打印完再打印10~100 之间的所有整数(PS:如果是这样的话,那就是 main 调用其他类的方法,属于单线程了)。
二、创建多线程的三种方式(小生写的比较繁缀罗嗦,建议阅读完之后,参考代码再阅读一次。)
第一种方式:
创建:编写一个类 MyThread1 (这里只是小生的自定义命名,方便与代码里名称一致)让它继承 Thread 类,
并把需要多线程运行的程序放到 public void run() 方法里。
启动:在主函数中,new 出 MyThread1 类的实例。
运行:调用 MyThread1 类的实例的 start() 方法即可。
第二种方式:
创建:编写一个类 MyThread2 让它实现 Runnable 接口,并且要重写 run() 方法(把需要多线程运行的程序放到 public void run() 方法里)。
启动:在主函数中,new 出 MyThread1 类的实例,
new 出Thread 类(带有 target 的构造方法),
把MyThread1 类的实例作为参数传入Thread 类的构造方法里。
运行:调用 Thread 类的实例的 start() 方法即可。
第三种方式:
创建:实现 Callable 接口(小生定义这个类为 MyCallable),并且实现 call() 方法,注意 call() 方法是有返回值的。
启动:new 出Callable 接口的实现类MyCallable,
new 出 FutureTask 类的实例 task,
把call() 方法的返回值放入FutureTask 类的构造方法里,
把 task 放入 new 出的 Thread 构造方法里。
运行:调用 Thread 类的实例的 start() 方法即可。
三种方式的利弊总结:
第一种方法好处:创建已经继承 Thread 类的类的实例,调用 start() 方法就能运行,代码写起来很方便,
当然缺点就是继承了Thread 父类,就没有办法继承其他的类了,扩展性不好。所以一般不建议用这种方式创建多线程。
第二种方法好处:继承接口,扩展性好。
弊端是相对的,第一种方式不好也是相对于第二种来说的,而第二种方式相比第三种方式来说,run() 方法没有返回值,并且不能申明抛出异常。
第三种方式好处上面已经说明,不好的就是编码很麻烦(小生太笨看了半天才搞明白)
四、创建多线程的三种方式的代码演示
说明:小生会对每种方式的每条线程进行重命名,只是为了方便代码运行效果演示。
java 默认线程的名称规则:java 中的 main 函数本身就是一个默认的主线程,名字为:main(在下面例子可以证实 main 线程在 jvm 中的默认名称)
如果自己定义的线程没有定义名称,则系统默认按照 Thread-0,Thread-1,……的命名方式进行命名。也可以通过对线程的构造方法进行重命名。
4.1 继承 Thread 类
偷窥一下 API 发现 Thread 类实现了 Runnable 接口,currentThread() 静态方法返回当前线程这个实例,再 getName() 一下就得到当前线程的名字了。
MyThread1 线程:
1 package com.test.threadDemo1; 2 3 public class MyThread1 extends Thread { 4 5 public MyThread1(String name) { 6 this.setName(name); 7 } 8 9 public void run() { 10 for(int i=20;i<=100;i++) { 11 System.out.println(getName()+"..."+i); 12 } 13 } 14 }
main 线程:
1 package com.test.threadDemo1; 2 /** 3 * 多线程的实现之一:继承 Thread 类 4 * @author Administrator 5 * 6 */ 7 public class ThreadDemo1 { 8 public static void main(String[] args) { 9 for (int i=1;i<=100;i++) { 10 System.out.println(Thread.currentThread().getName()+"..."+i); 11 12 if(i==30) { 13 MyThread1 myThread = new MyThread1("新线程"); 14 myThread.start(); 15 } 16 } 17 } 18 }
运行结果:(有交叉运行的情况发生,就是多线程了)
从结果可以看出,当 main 线程跑到 30 的时候还在“抢占” CPU
4.2 实现 Runable 接口
MyThread 线程:
1 package com.test.threadDemo1; 2 3 public class MyThread2 implements Runnable { 4 5 public MyThread2(String name) { 6 Thread.currentThread().setName(name); 7 } 8 9 @Override 10 public void run() { 11 for(int i=20;i<=100;i++) { 12 System.out.println(Thread.currentThread().getName()+"..."+i); 13 } 14 } 15 16 }
main 线程:
1 package com.test.threadDemo1; 2 /** 3 * 多线程的实现之二:实现 Runable 接口 4 * @author Administrator 5 * 6 */ 7 public class ThreadDemo2 { 8 public static void main(String[] args) { 9 for (int i=1;i<=100;i++) { 10 System.out.println(Thread.currentThread().getName()+"..."+i); 11 12 if(i==30) { 13 MyThread2 myThread = new MyThread2("新线程"); 14 15 Thread thread = new Thread(myThread); 16 17 thread.start(); 18 } 19 } 20 } 21 }
运行结果:
4.3 实现 Callable 接口和 Future 接口
Future接口的实现类是 FutureTask,它实现了 Runable 接口,所以能够作为 target 参数传到 Thread 类的构造方法里。
FutureTask(Callable<V> callable)
从构造方法里可以看出 FutureTask 的构造方法里传入 callable 的实现类就行了。
MyThread3 线程:
1 package com.test.threadDemo1; 2 3 import java.util.concurrent.Callable; 4 5 public class MyThread3 implements Callable<Integer> { 6 7 @Override 8 public Integer call() throws Exception { 9 int i = 20; 10 for(;i<=100;i++) { 11 System.out.println(Thread.currentThread().getName()+"..."+i); 12 } 13 return i; 14 } 15 16 }
main 线程:
1 package com.test.threadDemo1; 2 3 import java.util.concurrent.Callable; 4 import java.util.concurrent.FutureTask; 5 6 /** 7 * 多线程的实现之三:实现 Callable 接口和 Future 接口 8 * @author Administrator 9 * 10 */ 11 public class ThreadDemo3 { 12 public static void main(String[] args) { 13 14 Callable<Integer> myThread3 = new MyThread3(); 15 FutureTask<Integer> task = new FutureTask<Integer>(myThread3); 16 17 for (int i=1;i<=100;i++) { 18 System.out.println(Thread.currentThread().getName()+"..."+i); 19 20 if(i==30) { 21 22 Thread thread = new Thread(task,"新线程"); 23 thread.start(); 24 } 25 } 26 } 27 }
运行结果: