美文网首页Java工程师知识树
Java基础-线程-线程创建

Java基础-线程-线程创建

作者: HughJin | 来源:发表于2021-01-27 09:38 被阅读0次

Java工程师知识树 / Java基础


线程的创建

java.lang.Thread的构造方法API

创建线程的方式最终都是通过调用下面代码来创建线程:

/**
 * group - 线程组。 如果null并且有一个安全管理器,该组由SecurityManager.getThreadGroup()确定 。 如果没有安全管理员或SecurityManager.getThreadGroup()返回null ,该组将设置为当前线程的线程组。 
 * target - 启动此线程时调用其run方法的对象。 如果null ,这个线程的run方法被调用。 
 * name - 新线程的名称 
 * stackSize - 新线程所需的堆栈大小,或为零表示此参数将被忽略。
 */
public Thread(ThreadGroup group, Runnable target, String name,
              long stackSize) {
    init(group, target, name, stackSize);
}

Runnable接口是否在构造方法中传入为区分,上述构造方法分成两类创建线程对象:

  • 继承java.lang.Thread类,通过子类重写run方法实现业务逻辑处理后创建子类线程对象。
  • 入参传入java.lang.Runnable接口,当然可以使用Thread内的Runnable target,也可以使用自定义的Runnable接口实例。

然后这样分析的话很容易理解,传统意义上的,线程的创建有两种方式:

  • 1.继承java.lang.Thread类;不推荐使用,OOP单继承局限性。
  • 2.实现java.lang.Runnable接口;推荐使用,避免OOP单继承局限性,灵活方面,适合于多个线程处理同一资源的情况,比如买票,取号等。

线程创建与执行步骤:

  • 1.实现接口java.lang.Runnable重写其run方法;

其实java.lang.Thread也是实现了java.lang.Runnable接口。子类使用继承java.lang.Thread类形式创建线程,子类也重写了run方法,如果不重写,没有新创建线程的实际意义。

  • 2.创建线程对象:继承Thread类通过创建子类对象,实现Runnable接口通过new Thread(runnable)创建线程对象。

  • 3.通过start()方法启动线程。

从设计模式上看,java.lang.Threadjava.lang.Runnable实际上是一种静态代理的实现方式。

方式一:继承java.lang.Thread类

继承Thread类的话,重写run方法,在run方法中定义需要执行的任务。

通过继承继承Thread类创建自己的线程 :

package com.thread.study;

public class Test {
    public static void main(String[] args)  {
        System.out.println("执行主线程名称:"+Thread.currentThread().getName());
        MyThread thread1 = new MyThread();
        thread1.start();
        MyThread thread2 = new MyThread();
        thread2.run();
    }
}
 
 
class MyThread extends Thread{

    @Override
    public void run() {
        System.out.println("当前执行的线程名:"+Thread.currentThread().getName());
    }
}

运行结果:

执行主线程名称:main
当前执行的线程名:main
当前执行的线程名:Thread-0

结论:

  • 通过run方法调用并不会创建新的线程,而是在主线程中直接运行run方法,跟普通的方法调用没有任何区别
  • 虽然thread1的start方法调用在thread2的run方法前面调用,但是先输出的是thread2的run方法调用的相关信息,说明新线程创建的过程不会阻塞主线程的后续执行

方式二:实现java.lang.Runnable接口

通过实现Runnable接口来实现必须重写其run方法,在run方法中定义需要执行的任务。

package com.thread.study;

public class TestRunnable {
    public static void main(String[] args)  {
        System.out.println("执行主线程名称:"+Thread.currentThread().getName());
        MyRunnable runnable = new MyRunnable();
        new Thread(runnable).start();
        new Thread(runnable,"张三").start();
        new Thread(runnable,"李四").start();
    }
}
 
 
class MyRunnable implements Runnable{

    @Override
    public void run() {
        System.out.println("当前执行的线程名:"+Thread.currentThread().getName());
    }
}

必须将Runnable作为Thread类的参数,然后通过Thread的start方法来创建一个新线程来执行该子任务。

扩展:JDK针对Thread与Runnable的API

java.lang.Thread

public class Thread implements Runnable

线程是程序中执行的线程。Java虚拟机允许应用程序同时执行多个执行线程。

每个线程都有优先权。 具有较高优先级的线程优先于优先级较低的线程执行。 每个线程可能也可能不会被标记为守护程序。 当在某个线程中运行的代码创建一个新的Thread对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护线程。

当Java虚拟机启动时,通常有一个非守护进程线程(通常调用某些指定类的名为main的方法)。 Java虚拟机将继续执行线程,直到发生以下任一情况:

  • 已经调用了Runtime类的exit方法,并且安全管理器已经允许进行退出操作。
  • 所有不是守护进程线程的线程都已经死亡,无论是从调用返回到run方法还是抛出超出run方法的run

java.lang.Runnable

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
  • Runnable接口应由任何类实现,其实例将由线程执行。该类必须定义一个无参数的方法,称为run
  • Runnable接口旨在为希望在活动时执行代码的对象提供一个通用协议。

创建线程实现图片下载实例

package com.thread.study;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

public class TestDownLoadPictures {

    public static void main(String[] args) {

        PictureThread pictureThread = new PictureThread("https://img.haomeiwen.com/i25399192/f67ba4ca2a436ef1.png","E:\\study\\resource\\1.png");
        PictureThread pictureThread2 = new PictureThread("https://img.haomeiwen.com/i25399192/f67ba4ca2a436ef1.png","E:\\study\\resource\\2.png");
        PictureThread pictureThread3 = new PictureThread("https://img.haomeiwen.com/i25399192/f67ba4ca2a436ef1.png","E:\\study\\resource\\3.png");

        new Thread(pictureThread,"1.百度").start();
        new Thread(pictureThread2,"2.新浪").start();
        new Thread(pictureThread3,"3.搜狗").start();
    }
}


class PictureThread implements Runnable {

    private String picUrl;
    private String fileName;

    public PictureThread(String picUrl, String fileName) {
        this.picUrl = picUrl;
        this.fileName = fileName;
    }

    @Override
    public void run() {
        try {
            FileUtils.copyURLToFile(new URL(picUrl), new File(fileName));
            System.out.println(Thread.currentThread().getName() + "执行" + picUrl + "的下载");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

执行结果:

3.搜狗执行https://img.haomeiwen.com/i25399192/f67ba4ca2a436ef1.png的下载
1.百度执行https://img.haomeiwen.com/i25399192/f67ba4ca2a436ef1.png的下载
2.新浪执行https://img.haomeiwen.com/i25399192/f67ba4ca2a436ef1.png的下载

使用线程池方式—Callable接口

JDK关于Callable的API

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

返回结果并可能引发异常的任务。实现者定义一个没有参数的单一方法,称为call

Callable接口类似于Runnable ,因为它们都是为其实例可能由另一个线程执行的类设计的。 然而,Runnable不返回结果,也不能抛出被检查的异常。

JDK关于ExecutorService(线程池类)中submit(Callable<T> task)方法的API

<T> Future<T> submit(Callable<T> task)

提交值返回任务以执行,并返回代表任务待处理结果的Future。 Future的get方法将在成功完成后返回任务的结果。
如果您想立即阻止等待任务,您可以使用

result = exec.submit(aCallable).get();

格式的

result = exec.submit(aCallable).get(); 

使用步骤:

1.创建线程池对象 eg:ExecutorService service = Executors.newFixedThreadPool(2);
2.创建Callable接口子类对象 eg:class MyCreateCallable implements Callable
3.提交Callable接口子类对象 eg:Future<String> future = service.submit(myCreateCallable)
4.关闭线程池 eg:service.shutdown();

使用示例:

package com.thread.study;

import java.util.concurrent.*;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        //创建线程池对象
        ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
        //创建Callable对象
        MyCreateCallable myCreateCallable = new MyCreateCallable();

        Future<String> future = service.submit(myCreateCallable);
        //注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
        try {
            System.out.println("使用Callable接口返回值:"+future.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        MyCreateRunnable myCreateRunnable = new MyCreateRunnable();
        //使用获取个教练
        service.submit(myCreateRunnable);
        //注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中

        //关闭线程池
        service.shutdown();
    }
}

// Callable接口实现类,call方法可抛出异常、返回线程任务执行完毕后的结果
class MyCreateCallable implements Callable {
    @Override
    public String call() throws Exception {
        System.out.println("我要一个教练:call");
        Thread.sleep(2000);
        System.out.println("教练来了: " + Thread.currentThread().getName());
        System.out.println("教我游泳,交完后,教练回到了游泳池");
        return "我是通过Callable创建的";
    }
}

// Runnable接口实现类,无法返回执行结果
class MyCreateRunnable implements Runnable {

    @Override
    public void run() {
        System.out.println("我要一个教练:call");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("教练来了: " + Thread.currentThread().getName());
        System.out.println("教我游泳,交完后,教练回到了游泳池");
    }
}

执行结果:

我要一个教练:call
教练来了: pool-1-thread-1
教我游泳,交完后,教练回到了游泳池
使用Callable接口返回值:我是通过Callable创建的
我要一个教练:call
教练来了: pool-1-thread-2
教我游泳,交完后,教练回到了游泳池

相关文章

  • 面试题整理

    Java基础 创建线程的方式有哪些? 继承Thread类创建线程类 通过Runnable接口创建线程类 通过Cal...

  • 【java基础】线程

    java基础 线程 参考详细:Java之多线程 一、创建线程 创建线程常见有两种方式,另外有两种新增方式 1.Th...

  • 第5章 多线程编程

    第5章 多线程编程 5.1 线程基础 5.1.1 如何创建线程 在java要创建线程,一般有==两种方式==:1)...

  • Java面试题总结(上)

    多线程、并发及线程的基础问题 1)Java 中能创建 volatile 数组吗?能,Java 中可以创建 vola...

  • Java 面试题及答案

    多线程、并发及线程的基础问题 1)Java 中能创建 Volatile 数组吗? 能,Java 中可以创建 vol...

  • 2016java面试题总结

    多线程、并发及线程的基础问题: 1)Java 中能创建 volatile 数组吗? 能,Java 中可以创建 vo...

  • Java程序员不得不会的124道面试题(含答案)

    多线程、并发及线程的基础问题 1)Java 中能创建 volatile 数组吗? 能,Java 中可以创建 vol...

  • 133道 Java 面试题及答案

    多线程、并发及线程的基础问题 1)Java 中能创建 volatile 数组吗? 能,Java 中可以创建 vol...

  • Java常见问题

    一、多线程、并发及线程的基础问题 1)Java 中能创建 Volatile 数组吗? 能,Java 中可以创建 v...

  • JAVA面试题 1

    1. 多线程、并发及线程的基础问题 1)Java 中能创建 volatile 数组吗?能,Java 中可以创建 v...

网友评论

    本文标题:Java基础-线程-线程创建

    本文链接:https://www.haomeiwen.com/subject/ubaczktx.html