实现多线程的方法
package threadcoreknowledge.createthreads;
/**
* @author gaston
* 用runnable方式创建线程
*/
public class RunnableStyle implements Runnable{
@Override
public void run() {
System.out.println("用Runnable方法实现线程");
}
public static void main(String[] args) {
Thread thread = new Thread(new RunnableStyle());
thread.start();
}
}
------------------------------------------------------------------------
package threadcoreknowledge.createthreads;
/**
* @author gaston
* 用Thread方式实现线程
*/
public class ThreadStyle extends Thread{
public static void main(String[] args) {
new ThreadStyle().start();
}
@Override
public void run(){
System.out.println("用Thread方法实现线程");
}
}
以上两种方式对比,使用Runnable比较好:
1.Thread方式的run主要逻辑方法和Thread创建,thread类应该是解耦的,不能把两个事情混为一谈
2.使用Thread方式每次开启一个任务就要启动一个线程,而新建一个独立的线程这样的损耗是比较大的,他需要创建,执行和销毁,runnable可以使用线程池工具
3.继承了Thread后不能继承其他类,不能多继承
查看一下run方法的源码:
package threadcoreknowledge.createthreads;
/**
* @author gaston
* 测试同时使用Runnable和Tread启动
*/
public class BothRunnableThread {
public static void main(String[] args) {
new Thread(new Runnable(){
@Override
public void run() {
System.out.println("我是Runnable启动的");
}
}){
@Override
public void run() {
System.out.println("我是Thread启动的");
}
}.start();
}
}
/**
运行结果:
我是Thread启动的
说明:在main方法首先new一个thread匿名内部类,并且重写Thread的run方法,同时创建Runnable匿名内部类并重写run方法,这里Runnable也是作为了Thread的参数,参考上边源码run方法要么执行target.run()要么就是run()方法被重写,在这里是被Thread的run重写了方法里边没有源码原始的逻辑了也就不可能会是target.run()也就是不可能是Runnable线程输出的内容
**/
总结:如果要问启动线程的方法有几种,最精准的描述?
答:1.通常我们可以分为两类,根据oracle官方说明就是这样的,继承Thread类和实现Runnable接口
2.准确的说,创建线程只有一种方式就是构造Thread类(源码追踪也是这样的最终都会定位到Thread类下的run方法),而实现线程的执行单元有两种方式:
方法一,实现Runnable接口重写run方法,并把Runnable实例传给Thread类;
方法二,继承Thread类,重写Thread的run方法。
package threadcoreknowledge.createthreads.wrongways;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author gaston
* 验证错误说法,使用线程池启动线程
*/
public class ThreadPool5 {
public static void main(String[] args) {
ExecutorService executorService =
Executors.newCachedThreadPool();
for(int i =0; i < 20; i++){
executorService.submit(new Task(){
});
}
}
}
class Task implements Runnable{
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
结果:
pool-1-thread-13
pool-1-thread-4
pool-1-thread-17
pool-1-thread-16
pool-1-thread-6
pool-1-thread-18
pool-1-thread-12
pool-1-thread-19
pool-1-thread-2
pool-1-thread-14
pool-1-thread-5
pool-1-thread-7
pool-1-thread-8
pool-1-thread-1
pool-1-thread-11
pool-1-thread-9
pool-1-thread-10
pool-1-thread-3
pool-1-thread-15
pool-1-thread-20
线程池源码追踪:
错误说法:通过Callable和FutureTask创建线程,也算是一种新建线程的方式,错误原因是它们最终也是通过Thread或Runnable启动的线程,如图
package threadcoreknowledge.createthreads.wrongways;
/**
* @author gaston
* 演示java8新特性 lambda启动线程
*/
public class Lambda {
public static void main(String[] args) {
new Thread(() -> System.out.println(Thread.currentThread().getName())).start();
}
}
package threadcoreknowledge.createthreads.wrongways;
/**
* @author gaston
* 对比start和run启动方式
*/
public class StartAndRunMethod {
public static void main(String[] args) {
Runnable runnable = () -> {
System.out.println(Thread.currentThread().getName());
};
runnable.run();
new Thread(runnable).start();
}
}
结果:
main
Thread-0
run方法执行的是主线程,而不是像start方法那样新创建一个线程
start()方法启动一个新线程,但并不是立即运行这个线程,等待jvm和线程调度器来安排,他可以让主线程和新建的线程运行,父线程主线程启动后再去启动新线程,不能两次调用start方法。
检查线程状态,加入线程组,调用start0()
针对于run方法源码就是那个target.run()代码,没有参数的run方法就是一个简单的运行方法,它运行主线程。