美文网首页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基础-线程-线程创建

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