首先,定义thread类的子类并重写run()方法:
package com.zwwhnly.springbootaction.javabase.thread; public class myfirstthread extends thread { @override public void run() { for (int i = 0; i < 5; i++) { system.out.printf("[myfirstthread]输出:%d,当前线程名称:%s\n", i, getname()); } } }
然后,创建该子类的实例并调用start()方法启动线程:
package com.zwwhnly.springbootaction.javabase.thread; public class threadtest { public static void main(string[] args) { system.out.println("主线程开始执行,当前线程名称:" + thread.currentthread().getname()); thread firstthread = new myfirstthread(); firstthread.start(); system.out.println("主线程执行结束,当前线程名称:" + thread.currentthread().getname()); } }
运行结果如下所示:
主线程开始执行,当前线程名称:main
主线程执行结束,当前线程名称:main
[myfirstthread]输出:0,当前线程名称:thread-0
[myfirstthread]输出:1,当前线程名称:thread-0
[myfirstthread]输出:2,当前线程名称:thread-0
[myfirstthread]输出:3,当前线程名称:thread-0
[myfirstthread]输出:4,当前线程名称:thread-0
从运行结果可以看出以下2个问题:
firstthread.start();
,run()方法体中的代码并没有立即执行,而是异步执行的。查看thread类的源码,可以发现thread类实现了接口runnable:
public class thread implements runnable { // 省略其它代码 }
这里是重点,面试常问!
首先,定义runnable接口的实现类并实现run()方法:
package com.zwwhnly.springbootaction.javabase.thread; public class mysecondthread implements runnable { @override public void run() { for (int i = 0; i < 5; i++) { system.out.printf("[mysecondthread]输出:%d,当前线程名称:%s\n", i, thread.currentthread().getname()); } } }
然后,调用thread类的构造函数创建thread实例并调用start()方法启动线程:
package com.zwwhnly.springbootaction.javabase.thread; public class threadtest { public static void main(string[] args) { runnable target = new mysecondthread(); thread secondthread = new thread(target); secondthread.start(); } }
运行结果如下所示:
主线程开始执行,当前线程名称:main
主线程执行结束,当前线程名称:main
[mysecondthread]输出:0,当前线程名称:thread-0
[mysecondthread]输出:1,当前线程名称:thread-0
[mysecondthread]输出:2,当前线程名称:thread-0
[mysecondthread]输出:3,当前线程名称:thread-0
[mysecondthread]输出:4,当前线程名称:thread-0
可以看出,使用这种方式和继承thread类的运行结果是一样的。
首先,定义callable接口的实现类并实现call()方法:
package com.zwwhnly.springbootaction.javabase.thread; import java.util.random; import java.util.concurrent.callable; public class mythirdthread implements callable<integer> { @override public integer call() throws exception { thread.sleep(6 * 1000); return new random().nextint(); } }
然后,调用futuretask类的构造函数创建futuretask实例:
callable<integer> callable = new mythirdthread(); futuretask<integer> futuretask = new futuretask<>(callable);
最后,调用thread类的构造函数创建thread实例并调用start()方法启动线程:
package com.zwwhnly.springbootaction.javabase.thread; import java.util.concurrent.callable; import java.util.concurrent.executionexception; import java.util.concurrent.futuretask; public class threadtest { public static void main(string[] args) { system.out.println("主线程开始执行,当前线程名称:" + thread.currentthread().getname()); callable<integer> callable = new mythirdthread(); futuretask<integer> futuretask = new futuretask<>(callable); new thread(futuretask).start(); try { system.out.println("futuretask.isdone() return:" + futuretask.isdone()); system.out.println(futuretask.get()); system.out.println("futuretask.isdone() return:" + futuretask.isdone()); } catch (interruptedexception | executionexception e) { e.printstacktrace(); } system.out.println("主线程执行结束,当前线程名称:" + thread.currentthread().getname()); } }
运行结果如下所示:
主线程开始执行,当前线程名称:main
futuretask.isdone() return:false
-1193053528
futuretask.isdone() return:true
主线程执行结束,当前线程名称:main
可以发现,使用callable接口这种方式,我们可以通过futuretask.get()
获取到线程的执行结果,而之前的2种方式,都是没有返回值的。
注意事项:调用
futuretask.get()
获取线程的执行结果时,主线程会阻塞直到获取到结果。
阻塞效果如下图所示:
以下是重点,面试常问!
关于第2点,可以通过如下示例来理解。
假如我们总共有10张票(共享的资源),为了提升售票的效率,开了3个线程来售卖,代码如下所示:
package com.zwwhnly.springbootaction.javabase.thread; public class saleticketthread implements runnable { private int quantity = 10; @override public void run() { while (quantity > 0) { system.out.println(quantity-- + " is saled by " + thread.currentthread().getname()); } } }
public static void main(string[] args) { runnable runnable = new saleticketthread(); thread saleticketthread1 = new thread(runnable); thread saleticketthread2 = new thread(runnable); thread saleticketthread3 = new thread(runnable); saleticketthread1.start(); saleticketthread2.start(); saleticketthread3.start(); }
因为3个线程都是异步执行的,因此每次的运行结果可能是不一样,以下列举2次不同的运行结果。
第1次运行结果:
10 is saled by thread-0
8 is saled by thread-0
7 is saled by thread-0
5 is saled by thread-0
9 is saled by thread-1
3 is saled by thread-1
2 is saled by thread-1
1 is saled by thread-1
4 is saled by thread-0
6 is saled by thread-2
第2次运行结果:
10 is saled by thread-0
9 is saled by thread-0
8 is saled by thread-0
7 is saled by thread-0
6 is saled by thread-0
5 is saled by thread-0
3 is saled by thread-0
2 is saled by thread-0
4 is saled by thread-2
1 is saled by thread-1
如果将上面的saleticketthread修改成继承thread类的方式,就变成了3个线程各自拥有10张票,即变成了30张票,而不是3个线程共享10张票。
因为实现runnable接口的优势,基本上实现多线程都使用的是该种方式,所以我们将之前定义的myfirstthread也修改为实现runnable接口的方式:
package com.zwwhnly.springbootaction.javabase.thread; public class myfirstthread implements runnable { @override public void run() { for (int i = 0; i < 5; i++) { system.out.printf("[myfirstthread]输出:%d,当前线程名称:%s\n", i, thread.currentthread().getname()); } } }
然后仍然沿用之前定义的myfirstthread、mysecondthread,我们先看下调用start()的效果:
package com.zwwhnly.springbootaction.javabase.thread; public class threadtest { public static void main(string[] args) { system.out.println("主线程开始执行,当前线程名称:" + thread.currentthread().getname()); thread firstthread = new thread(new myfirstthread()); runnable target = new mysecondthread(); thread secondthread = new thread(target); firstthread.start(); secondthread.start(); system.out.println("主线程执行结束,当前线程名称:" + thread.currentthread().getname()); } }
运行结果(注意:多次运行,结果可能不一样):
主线程开始执行,当前线程名称:main
[myfirstthread]输出:0,当前线程名称:thread-0
[myfirstthread]输出:1,当前线程名称:thread-0
[mysecondthread]输出:0,当前线程名称:thread-1
主线程执行结束,当前线程名称:main
[mysecondthread]输出:1,当前线程名称:thread-1
[mysecondthread]输出:2,当前线程名称:thread-1
[mysecondthread]输出:3,当前线程名称:thread-1
[mysecondthread]输出:4,当前线程名称:thread-1
[myfirstthread]输出:2,当前线程名称:thread-0
[myfirstthread]输出:3,当前线程名称:thread-0
[myfirstthread]输出:4,当前线程名称:thread-0
可以看出,调用start()方法后,程序中有3个线程,分别为主线程main、thread-0、thread-1,而且执行顺序不是按顺序执行的,存在不确定性。
然后将start()方法修改为run()方法,如下所示:
firstthread.run(); secondthread.run();
此时的运行结果如下所示(多次运行,结果是一样的):
主线程开始执行,当前线程名称:main
[myfirstthread]输出:0,当前线程名称:main
[myfirstthread]输出:1,当前线程名称:main
[myfirstthread]输出:2,当前线程名称:main
[myfirstthread]输出:3,当前线程名称:main
[myfirstthread]输出:4,当前线程名称:main
[mysecondthread]输出:0,当前线程名称:main
[mysecondthread]输出:1,当前线程名称:main
[mysecondthread]输出:2,当前线程名称:main
[mysecondthread]输出:3,当前线程名称:main
[mysecondthread]输出:4,当前线程名称:main
主线程执行结束,当前线程名称:main
可以看出,调用run()方法后,程序中只有一个主线程,自定义的2个线程并没有启动,而且执行顺序也是按顺序执行的。
以下是重点,面试常问!
在文章前面的章节中(1.2 实现runnable接口 和1.3 实现callable接口),我们了解了如何使用runnable、callable接口来创建线程,现在我们分别看下runable和callable接口的定义,其中,runable接口的定义如下所示:
public interface runnable { public abstract void run(); }
callable接口的定义如下所示:
public interface callable<v> { v call() throws exception; }
由此可以看出,runnable和callable的区别主要有以下几点:
源码地址:,欢迎下载。
java thread 的 run() 与 start() 的区别
如果觉得文章写的不错,欢迎关注我的微信公众号:「申城异乡人」,所有博客会同步更新。
如果有兴趣,也可以添加我的微信:zwwhnly_002,一起交流和探讨技术。
如对本文有疑问, 点击进行留言回复!!
java8 map集合还可以这样根据value升降序,代码瞬间提升了bo格
springboot TCP socket通信远程监听采集数据.
网友评论