使用线程的正确姿势!

【声明】:本篇文章来自本人gitee仓库搬运至CSDN,https://gitee.com/genmers/md-notes

Java线程学习记录

一、实现线程的两种方法
二、两种方法的本质
三、线程池实现算不算一种新的方式
四、参考资料

一、实现线程的两种方法

官方认证的两种实现线程的正确方法

  • 方法一:实现Runnble接口
  • 方法二:继承Thread

两种方法的对比

方法1(实现Runnable接口)更好

假如我们重写的run方法都是这样

	@Override
	public void run() {
		System.out.println("创建了一个线程");
	}

1.从解耦的角度来看,run方法里的内容应该跟线程的创建运行的机制-也就是Thread类是解耦的

2.从资源消耗角度来看,如果用继承Thread类的方法,每次要使用线程都要新建一个独立的线程,而新建一个独立的线程(创建-执行-销毁)损耗的资源是比较大的

而使用Runnable,可以使用后续线程池之类的工具,可以减少消耗

3.由于Java不支持双继承,继承了Thread类就会导致这个类不能继承其他,大大限制了扩展性。

二、两种方法的本质

这是Thread类的run方法

    /* What will be run. */
    private Runnable target;
    
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

可以看到,Runnable方式的run方法是通过判断target是否为null,如果是runnable方式,target肯定不为空,就会执行target的run方法

而继承Thread类,子类重写run方法就会覆盖父类,就不会判断target,而是直接执行重写的方法

  • 方法一:调用Target.run()
  • 方法二:整个重写run方法
总结:
  • 通常来说可以分为两类
  • 准确的讲,创建线程只有一种方式就是构造Thread类,而实现线程的执行单元有两种方式

方法一:实现Runnable接口的run方法,并把Runnable实例传给Thread类

方法二:继承Thread类并重写run方法

三、线程池实现算不算一种新的方式

首先,我们知道线程池执行任务是使用execute方法,这个方法定义在Executor接口中

public interface Executor {
    void execute(Runnable command);
}

这个方法是没有返回值的,只接受一个Runnable类型的参数
如果有接收返回值的需求怎么办

我在ExecutorService这个接口中发现了以下方法

    <T> Future<T> submit(Callable<T> task);
  
    <T> Future<T> submit(Runnable task, T result);

    Future<?> submit(Runnable task);

可以看出,这个方法接收了一个Callable或者Runnable类型的参数,返回值是Future

我们先来看看Callable或者Runnable者两种接口

Callable

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

Runnable

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

可以看出

  1. Callable类型接收一个泛型参数,通过call方法返回一个该类型的值
  2. Runnable的run方法没有返回值
  3. Runnable虽然没有返回值,却可以调用submit(Runnable task, T result)自定义一个类型来接收结果
  4. Callable的call方法可以抛出异常,Runnable的run方法不行

Future

返回值Future也是一个接口, get是获取线程执行完任务返回值的方法

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
        }
具体关于Callable和Future相关可查看参考资料

回到++线程池实现算不算一种新的方式++这个问题

package wrongways;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 线程池方式实现线程
 */

public class ThreadPool {
	public static void main(String[] args) {
		ExecutorService executorService = Executors.newCachedThreadPool();
		for (int i=0;i<1000;i++){
			executorService.submit(new Task(){
			});
		}
	}
}

class Task implements Runnable{

	@Override
	public void run() {
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName());
	}
}

结果

pool-1-thread-8
pool-1-thread-12
pool-1-thread-4
pool-1-thread-9
pool-1-thread-13
pool-1-thread-15
pool-1-thread-5
...
pool-1-thread-999
pool-1-thread-1000

在Executors.java中搜索 new Thread,

    /**
     * The default thread factory
     */
    static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

从源码中我们可以看出线程池默认也是实现 ***ThreadFactory***接口,重写newThread方法,也是调用了Thread创建线程

四、参考资料

Java多线程-Callable的Future返回值的使用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值