东哥说java并发 第一集

实现多线程的方法

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方法的源码:

东哥说java并发 第一集_第1张图片

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

线程池源码追踪:

东哥说java并发 第一集_第2张图片

错误说法:通过Callable和FutureTask创建线程,也算是一种新建线程的方式,错误原因是它们最终也是通过Thread或Runnable启动的线程,如图

东哥说java并发 第一集_第3张图片

 

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()

东哥说java并发 第一集_第4张图片

东哥说java并发 第一集_第5张图片

东哥说java并发 第一集_第6张图片

针对于run方法源码就是那个target.run()代码,没有参数的run方法就是一个简单的运行方法,它运行主线程。

 

你可能感兴趣的:(JAVA知识)