Java基础-线程-线程创建

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://upload-images.jianshu.io/upload_images/25399192-f67ba4ca2a436ef1.png","E:\\study\\resource\\1.png");
        PictureThread pictureThread2 = new PictureThread("https://upload-images.jianshu.io/upload_images/25399192-f67ba4ca2a436ef1.png","E:\\study\\resource\\2.png");
        PictureThread pictureThread3 = new PictureThread("https://upload-images.jianshu.io/upload_images/25399192-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://upload-images.jianshu.io/upload_images/25399192-f67ba4ca2a436ef1.png的下载
1.百度执行https://upload-images.jianshu.io/upload_images/25399192-f67ba4ca2a436ef1.png的下载
2.新浪执行https://upload-images.jianshu.io/upload_images/25399192-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
教我游泳,交完后,教练回到了游泳池
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容