线程创建的四种方法:
创建线程的常用四种方法:
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
- 线程池方法创建如用Executor框架
通过继承Thread类或者Runnable接口、Callable接口都可以实现多线程,不过实现Runnable接口与实现Callable接口的方式基本相同,只是Callable接口里定义的方法有返回值,可以声明抛出异常而已。
采用实现Runnable、Callable接口的方式创建线程的优缺点:
优点:线程类只是实现了Runnable或者Callable接口,还可以继承其他类。这种方式下,多个线程可以共享一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好的体现了面向对象的思想。
缺点:编程稍微复杂一些,如果需要当我当前该线程,则必须使用Thread.currentThread()方法
采用继承Thread类的方法创建线程的优缺点:
优点:编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获取当前线程
缺点:因为线程类已经继承了Thread类,Java语言是单继承的,所以不能再继承其他父类。
采用线程池创建线程的优点:
1、提高响应速度----减少了创建新线程的时间
2、降低资源消耗----重复利用线程池中线程,不需要每次都创建
3、便于线程管理
第一种 继承Thread类
通过继承Thread类来创建并启动多线程的一般步骤如下
1、定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。
2、创建Thread子类的实例,也就是创建了线程对象
3、启动线程,即调用线程的start()方法
//1. 创建一个继承于Thread类的子类
class MyThread extends Thread{
// 2. 重写Tread类的run()
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
if(i%2==0){
// System.out.println(i);
//输出线程名
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
//3. 创建Tread类的子类的对象
MyThread t = new MyThread();
//4. 通过此对象调用start(): A :启动当前线程 B: 调用当前线程的run()方法
t.start();
// 第一个问题: 这是调方法 还是在主线程中执行的 我们不能直接调用run()的方式启动线程
// t.run();
//第二个问题:再启动一个线程 不可以让已经start()了的线程去执行 不然会报错IllegalThreadStateException
// t.start();
MyThread t2 = new MyThread();
t2.start();
// 证明线程有交互性
for (int j = 0; j < 50; j++) {
if(j%2!=0){
System.out.println(Thread.currentThread().getName()+":"+j+"*****");
}
}
//创建Thread类的匿名子类的方式
new Thread(){
@Override
public void run() {
for (int k = 0; k <10 ; k++) {
System.out.println(Thread.currentThread().getName()+": LP");
}
}
}.start();
}
}
运行部分截图:
第二种 实现Runnable接口
通过实现Runnable接口创建并启动线程一般步骤如下:
1、定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体
2、创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象
3、第三部依然是通过调用线程对象的start()方法来启动线程
// 1、创建一个类实现Runnable接口
class Number implements Runnable{
// 2. 重写Runnable接口的run()
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
if(i%2==0){
// System.out.println(i);
//输出线程名
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public class ThreadRunnable {
public static void main(String[] args) {
// 3、创建实现Runnable接口类的对象
Number number=new Number();
// 4、将对象作为Thread类的参数
Thread thread=new Thread(number,"线程1");
thread.start();
}
}
运行结果部分截图:
第三种 实现Callable接口
1、创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
2、使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
3、使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
4、调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
class cal implements Callable{
private int num=0;
@Override
public Integer call() throws Exception {
for (int i = 0; i <100 ; i++) {
if(i%2==0){
// System.out.println(i);
//输出线程名
System.out.println(Thread.currentThread().getName()+":"+i);
num+=i;
}
}
return num;
}
}
public class CallableThread {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 将接口Callable实现类的对象作为FutureTask的参数
FutureTask futureTask=new FutureTask(new cal());
// 将FutureTask对象作为Thread的参数
new Thread(futureTask, "窗口1").start();
// 还可以获取Callable中call方法的返回值
System.out.println("num的值:"+futureTask.get());
}
}
运行结果部分截图:
第四种 通过线程池如用Executor框架
1.5后引入的Executor框架的最大优点是把任务的提交和执行解耦。要执行任务的人只需把Task描述清楚,然后提交即可。这个Task是怎么被执行的,被谁执行的,什么时候执行的,提交的人就不用关心了。具体点讲,提交一个Callable对象给ExecutorService(如最常用的线程池ThreadPoolExecutor),将得到一个Future对象,调用Future对象的get方法等待执行结果就好了。Executor框架的内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。因此,在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免this逃逸问题——如果我们在构造器中启动一个线程,因为另一个任务可能会在构造器结束之前开始执行,此时可能会访问到初始化了一半的对象用Executor在构造器中。
/** 1、继承Thread类创建线程
* 1) 定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run方法也称为线程执行体。
* 2) 创建Thread子类的实例,也就是创建了线程对象
* 3) 启动线程,调用线程的start()方法
*/
class MyThread1 extends Thread{
@Override
public void run() {
// 重写run方法
System.out.println(Thread.currentThread().getName()+"你开始拉屎?...\t"+new Date());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"你结束拉屎?...\t"+new Date());
}
}
/** 2、实现Runnable接口创建线程
* 1) 定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体
* 2) 创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象
* 3) 第三步依然是通过调用线程对象的start()方法来启动线程
*/
class MyThread2 implements Runnable{
@Override
public void run() {
// 重写run方法
System.out.println(Thread.currentThread().getName()+"你开始吃饭?...\t"+new Date());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"你结束吃饭?...\t"+new Date());
}
}
/** 3、实现Callable接口创建线程
*
*/
class MyThread3 implements Callable {
private Integer sum=0;
@Override
public Integer call() throws Exception {
for(int i=0;i<=100;i++){
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"--->"+i);
sum+=i;
}
return sum;
}
}
public class ThreadPool {
public static void main(String[] args) {
// 1、提供指定的线程数量的线程池
ExecutorService executorService= Executors.newFixedThreadPool(10);
// ThreadPoolExecutor service=(ThreadPoolExecutor) executorService;
// // 设置线程池的属性
// service.setCorePoolSize(15);
executorService.execute(new MyThread1()); // 执行Runnable接口的实现类
executorService.execute(new MyThread2()); // 执行Runnable接口的实现类
Future submit = executorService.submit(new MyThread3());// 执行Callable接口的实现列
try {
System.out.println(submit.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
// 关闭线程
executorService.shutdown();
}
}
运行结果部分截图: