java线程创建的三种方式
第一种:继承Thread
过程大致如下:
- 继承tread
- 重写run方法
- 使用对象.start()方法启动线程
实例:
/**
* 线程的第一种方式:
* 1.继承tread
* 2.重写run方法
* 3.使用对象.start()方法启动线程
*
* @author Administrator
*
*/
public class Test1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("test1-->"+i);
}
}
public static void main(String[] args) {
new Test1().start();
for (int i = 0; i < 1000; i++) {
System.out.println("main-->"+i);
}
}
}
结果:
....
main-->74
main-->75
main-->76
main-->77
test1-->0
main-->78
test1-->1
main-->79
test1-->2
main-->80
....
从上面我们看出一个问题,那就是主方法中的线程执行到i=77才开始执行test的这一条线程,原因在与线程的调度不是由我们程序控制,而是由CPU自己去调度完成,我们只是告诉CPU这里有一个线程需要执行,至于什么时候执行,是CPU自己的事情。
总结以上可得出:
1.进程的执行不是人为控制的,是由我们的电脑的CPU去分配的
2.我们程序的主方法也是一个线程,或者说是一条执行路径,除了主方法外,JVM中还有GC(垃圾回收)和Exception(异常处理),所以java一开始就会有有至少3条执行路径。
这种方式虽然和简便,但是有一点不足时我们是继承了Thread类,那么我们就不能再继承其他类了,这一点在开发中很不利,那么我们接下来将第二种方式。
第二种:实现runable接口
过程大致如下:
- 1.implements Runnable
- 2.重写run方法
- 3.使用线程代理执行线程体
实例
/**
* 第二种方式:实现Runnable接口
* 1.implements Runnable
* 2.重写run方法
* 3.使用线程代理执行线程体。
* @author Administrator
*
*/
public class Test2 implements Runnable{
public static void main(String[] args){
Thread thr=new Thread(new Test2());
thr.start();
for(int i=0;i<100;i++){
System.out.println("main--->"+i);
}
}
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("test--->"+i);
}
}
}
结果:
...
main--->6
test--->6
test--->7
main--->7
test--->8
main--->8
test--->9
....
和我们预想的一样,也是主线程执行一点,自己定义的线程执行一部分。
这种方式显然是比第一种方式好,我们可以继承我们自己的类,还可以实现其他的接口(因为java语言可可以实现多个接口但是只能继承一个父类)
但是还是有问题:
- 当我们的线程体中有异常出现的时候,我们在外部调用的时候是不能察觉将来后有异常产生的
- 而且有时候我们希望线程执行结束后能给我们返回一个结果,而上面的两种方式都不能满足我们的要求
那么我现在给大家带来第三种创建线程的方式
第三种,实现callable接口
大致流程如下:
- 实现Callable接口
- 重写call方法
- 调用方式:
- 借助 执行调度服务ExecuteorService获取Future对象
- Future future1 = ser.submit(new 线程类); 使用这种方法获取一个未来返回的对象
- ser.shutdown();// 关闭调度服务
实例
/**
* 第三种方式: 1.实现Callable接口
* 2.重写call方法
* 3.调用方式:借助 执行调度服务ExecuteorService获取Future对象
* Future<T> future1 = ser.submit(new 线程类); 使用这种方法获取一个未来返回的对象
* ser.shutdown();// 关闭调度服务
* @author Administrator
*
* @param <T>
*/
public class Test3 implements Callable<Integer> {
private int step;
private int time; // 休眠时间
private boolean flage = true;
public void setFlage(boolean flage) {
this.flage = flage;
}
public Test3(int time) {
super();
this.time = time;
}
@Override
public Integer call() throws Exception {
while (flage) {
Thread.sleep(time);
step++;
}
return step;
}
public static void main(String[] args) throws InterruptedException,
ExecutionException {
ExecutorService ser = Executors.newFixedThreadPool(2);// 开启调度服务 开启两个调度
Test3 t1 = new Test3(1000);
Test3 t2 = new Test3(500);
Future<Integer> future1 = ser.submit(t1);
Future<Integer> future2 = ser.submit(t2);
Thread.sleep(4000);
t1.setFlage(false);
t2.setFlage(false);
int result1 = future1.get();
int result2 = future2.get();
System.out.println("第一个的返回结果是" + result1);
System.out.println("第二个的返回结果是" + result2);
ser.shutdown();// 关闭调度服务
}
}
我们发现使用这种方法我们的程序变复杂了,但是你可以发现,我们可以得到线程体执行完成之后的一个返回值,而且我们还可以得到线程体出现的异常信息。
以上是三种线程的创建方式,大家可以好好琢磨琢磨,特别是第三种,前面两种都是我们课本中涉及的东西,但是第三种才是我们工作中真正需要用到的服务器开发多线程处理的解决之道。