线程的实现方法


我自己对这个概念当时记得,长时间不用,然后又忘了,在这里做笔记给自己回顾使用

通过集成java.lang.Thread线程类来创建一个线程
package com.jay.Thread;

/**
 * @version 0.0.1
 * @program: spring-poi-demo
 * @description: 通过集成java.lang.Thread线程类来创建一个线程
 * @author: huangzq
 * @create: 2020-09-04 08:21
 */
public class TestThread {

    public static void main(String[] args) {
        /**
         * 控制台输出结果:
         * 主线程ID是: 1
         * 名称线程2的线程ID是:1
         * 名称线程1的线程ID是:11
         * 结论:
         * 1、主线程和线程2的线程ID相同,说明直接调用run()方法不会创建新的线程,而是在主线程中直接调用run()方法,普通的方法调用
         * 2、线程1先调用start()方法,而后线程2调用run()方法,最终却线程2先于线程1输出,说明新建的线程并不会影响主线程的执行顺序
         */

        System.out.println("主线程ID是: " + Thread.currentThread().getId());

        Thread t1 = new MyThread("线程1");
        t1.start();

        Thread t2 = new MyThread("线程2");
        /*直接调用run()方法*/
        t2.run();
    }

    /**
     * 自定义线程
     */
    static class MyThread extends Thread {
        /*线程名称*/
        private String name;

        public MyThread(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            System.out.println("名称:" + name + "的线程ID是:" + Thread.currentThread().getId());
        }
    }
}

通过实现java.lang.Runnable接口来创建一个线程
package com.jay.Thread;

/**
 * @version 0.0.1
 * @program: spring-poi-demo
 * @description: 通过实现java.lang.Runnable接口来创建一个线程
 * @author: huangzq
 * @create: 2020-09-04 08:28
 */
public class TestRunnable {

    /**
     * 其实不管是通过继承Thread类,还是实现Runnable接口的方式,都可以创建一个线程,其结果都是一样的。
     * 区别在于:
     *
     * 实现Runnable的方式需要将实现Runnable接口的类作为参数传递给Thread,然后通过Thread类调用Start()方法来创建线程,
     * 直接继承Thread类的话代码更简洁,也更容易理解,但是由于JAVA被设计为只支持单继承,所以如果要继承其他类的同时需要实现线程那就只能实现Runnable接口了,
     * 这里更推荐实现Runnable接口,实现Runnable的方式更为灵活一些。
     *
     * @param args
     */
    public static void main(String[] args) {

        System.out.println("主线程的ID是: " + Thread.currentThread().getId());

        MyRunnable r1 = new MyRunnable("线程1");

        Thread t1 = new Thread(r1);
        t1.start();

        MyRunnable r2 = new MyRunnable("线程2");
        Thread t2 = new Thread(r2);
        /*直接调用run()方法,并不会创建新线程*/
        t2.run();
    }

    static class MyRunnable implements Runnable{

        private String name;

        public MyRunnable(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            System.out.println("名字:" + name + "的线程ID是: " + Thread.currentThread().getId());
        }
    }

}

带返回值的线程接口Callable
package com.jay.Thread;

import java.util.concurrent.*;

/**
 * @version 0.0.1
 * @program: spring-poi-demo
 * @description: 带返回值的线程接口Callable
 * @author: huangzq
 * @create: 2020-09-04 08:57
 */
public class TestCallable {

    /**
     * 线程1返回了东西...
     * 线程2返回了东西...
     * 线程3返回了东西...
     * @param args
     */
    public static void main(String[] args) {
        //创建一个线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(3);

        //创建三个有返回值的任务
        MyCallable c1 = new MyCallable("线程1");
        MyCallable c2 = new MyCallable("线程2");
        MyCallable c3 = new MyCallable("线程3");

        Future f1 = threadPool.submit(c1);
        Future f2 = threadPool.submit(c2);
        Future f3 = threadPool.submit(c3);

        try {
            System.out.println(f1.get().toString());
            System.out.println(f2.get().toString());
            System.out.println(f3.get().toString());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }

    /**
     * 实现带返回值的接口
     */
    static class MyCallable implements Callable {

        private String name;

        public MyCallable(String name) {
            this.name = name;
        }

        @Override
        public Object call() throws Exception {
            return name + "返回了东西...";
        }
    }
}

Java多线程之Callable接口的实现
package com.jay.Thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @version 0.0.1
 * @program: spring-poi-demo
 * @description: Java多线程之Callable接口的实现
 * @author: huangzq
 * @create: 2020-09-04 09:06
 */
/*
 * 一、创建执行线程的方式三:实现 Callable 接口。 相较于实现 Runnable 接口的方式,方法可以有返回值,并且可以抛出异常。
 *
 * 二、执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。  FutureTask 是  Future 接口的实现类
 */
public class TestCallableTwo {

    /**
     * 例子可以看到: Callable 和 Runnable接口的区别
     *
     *   (1)Callable规定的方法是call(),而Runnable规定的方法是run().
     *   (2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
     *   (3)call()方法可抛出异常,而run()方法是不能抛出异常的。
     *   (4)运行Callable任务可拿到一个Future对象, Future表示异步计算的结果。
     *   它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
     *   通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
     *    Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。
     * @param args
     */
    public static void main(String[] args) {

        ThreadDemo td = new ThreadDemo();

        //1.执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。
        FutureTask<Integer> result = new FutureTask<>(td);

        new Thread(result).start();

        //2.接收线程运算后的结果
        try {
            //FutureTask 可用于 闭锁 类似于CountDownLatch的作用,在所有的线程没有执行完成之后这里是不会执行的
            Integer sum = result.get();
            System.out.println(sum);
            System.out.println("------------------------------------");
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    static class ThreadDemo implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            int sum = 0;
            for (int i = 0; i <= 100000; i++) {
                sum += i;
            }
            return sum;
        }

    }
}

join()加入线程,会让主线程等待
package com.jay.Thread;

/**
 * @version 0.0.1
 * @program: spring-poi-demo
 * @description: join()加入线程
 * @author: huangzq
 * @create: 2020-09-04 08:37
 */
public class TestJoin {

    /**
     * 执行结果:
     * main主线程: main ====> 0
     * main主线程: main ====> 1
     * 当前线程: 线程1 ===> 0
     * main主线程: main ====> 2
     * 当前线程: 线程1 ===> 1
     * 当前线程: 线程1 ===> 2
     * 当前线程: 线程1 ===> 3
     * 当前线程: 线程1 ===> 4
     * main主线程: main ====> 3
     * main主线程: main ====> 4
     * 当前线程: 线程2 ===> 0
     * 当前线程: 线程2 ===> 1
     * 当前线程: 线程2 ===> 2
     * 当前线程: 线程2 ===> 3
     * 当前线程: 线程2 ===> 4
     * main主线程: main ====> 5
     * main主线程: main ====> 6
     * main主线程: main ====> 7
     * main主线程: main ====> 8
     * main主线程: main ====> 9
     *
     * 结论:
     * 1、使用了join()方法之后,主线程会等待子线程结束之后才会结束,join在代码中可以注释来测试看结果
     */
    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new MyJoinThread("线程1");
        t1.start();
        t1.join();

        for (int i = 0; i < 10; i++){
            if(i == 5){
                Thread t2 = new MyJoinThread("线程2");
                t2.start();
                t2.join();
            }
            System.out.println("main主线程: " + Thread.currentThread().getName() + " ====> " + i);
        }
    }


    static class MyJoinThread extends Thread{

        public MyJoinThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            for (int i = 0; i < 5; i++){
                System.out.println("当前线程: " + Thread.currentThread().getName() + " ===> " + i);
            }
        }
    }
}

sleep()线程休眠
package com.jay.Thread;

/**
 * @version 0.0.1
 * @program: spring-poi-demo
 * @description: sleep()线程休眠
 * @author: huangzq
 * @create: 2020-09-04 08:32
 */
public class TestSleep {

    private int i = 10;
    private Object object = new Object();

    /**
     * 线程: Thread-0开始执行,i的值为:11
     * 线程: Thread-0准备进入休眠状态...
     * 线程: Thread-0休眠结束...
     * 线程: Thread-0继续执行,i的值为===========>:12
     * 线程: Thread-1开始执行,i的值为:13
     * 线程: Thread-1准备进入休眠状态...
     * 线程: Thread-1休眠结束...
     * 线程: Thread-1继续执行,i的值为===========>:14
     * 结论:
     * 1、当Thread-0进入休眠状态,Thread-1并没有马上执行,而是等待Thread-0休眠结束释放了对象锁才继续执行
     * 2、当调用sleep()方法时,必须捕获异常或者向上抛出异常,当线程休眠结束并不会马上执行,而是进入就绪状态,等待CPU的再次调度,调用Sleep()方法相当于是进入了阻塞状态
     * 3、补充:这个时候可考察sleep和wait的区别,他们所属对象不同,sleep这个时候不会释放锁,而wait会释放,不会阻塞,synchronized我在上篇的文章中详细介绍了
     */
    public static void main(String[] args) {
        TestSleep testSleep = new TestSleep();
        Thread t1 = testSleep.new MyTestThread();
        t1.start();

        Thread t2 = testSleep.new MyTestThread();
        t2.start();
    }

    class MyTestThread extends Thread{
        @Override
        public void run() {
            synchronized (object){
                i++;
                System.out.println("线程: " + Thread.currentThread().getName() + "开始执行,i的值为:" + i);
                System.out.println("线程: " + Thread.currentThread().getName() + "准备进入休眠状态...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("线程: " + Thread.currentThread().getName() + "休眠结束...");
                i++;
                System.out.println("线程: " + Thread.currentThread().getName() + "继续执行,i的值为===========>:" + i);
            }
        }
    }
}

yeild()让出CPU执行权限
package com.jay.Thread;

/**
 * @version 0.0.1
 * @program: spring-poi-demo
 * @description: yeild()让出CPU执行权限
 * @author: huangzq
 * @create: 2020-09-04 08:48
 */
public class TestYield {

    /**
     * 执行结果:
     * 名字: 线程1执行
     * 名字: 线程2执行
     * 线程2结果num: 1065788928计算耗时: 8毫秒
     * 线程1结果count: 1784293664计算耗时: 138毫秒
     * 结论:
     * 1、调用yield()方法是为了让当前线程让出CPU执行权限,从而可以让CPU去执行其他线程,它和sleep()方法类似同样是不会释放对象锁,
     * 但是yield()不会控制具体的交出CPU权限的时间,同时也只能让具有相同优先级的线程获得CPU执行时间的机会
     * 2、调用yield()方法并不会让当前线程进入阻塞状态,而只是进入就绪状态,只需要等待重新获取CPU的时间片,而Sleep()则会进入阻塞状态
     */
    public static void main(String[] args) {

        MyYieldThread t = new MyYieldThread("线程1");
        t.start();

        MyYieldThread2 t2 = new MyYieldThread2("线程2");
        t2.start();

        System.out.println("主线程名称:"+Thread.currentThread().getName());
    }

    /**
     * 线程1的
     */
    static class MyYieldThread extends Thread{
        private String name;

        public MyYieldThread(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            System.out.println("名字: " + name + "执行");
            long start = System.currentTimeMillis();
            int count = 0;
            for (int i = 0; i < 1000000; i++){
                count = count + (i + 1);
                //执行到这里就让了cup给线程2执行了
                Thread.yield();
            }

            long end = System.currentTimeMillis();
            System.out.println(name + "结果count: " + count + ",计算耗时: " + (end - start) + "毫秒");
        }
    }

    /**
     * 线程2的
     */
    static class MyYieldThread2 extends Thread{
        private String name;

        public MyYieldThread2(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            System.out.println("名字: " + name + "执行");
            long start = System.currentTimeMillis();
            int num = 0;
            for (int i = 0; i < 10000000; i++){
                num += i * 8;
            }
            long end = System.currentTimeMillis();
            System.out.println(name + "结果num: " + num + ",计算耗时: " + (end - start) + "毫秒");
        }
    }
}

从下面的代码例子得到结论

1、Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值。

2、通常在使用Callable的时候,也会涉及到Future,一般配合一起使用,一个产生结果,一个拿到结果。

3、假设有一个很耗时的返回值需要计算,并且这个返回值不是立刻需要的话,那么就可以使用这个组合,用另一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其它的操作,等到需要这个返回值时,再通过Future得到最终计算结果

参考:https://www.jianshu.com/p/94c1c34053d0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值