Thread 之 run() 方法

案例代码一

@Slf4j
public class Client {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }     
}

class MyThread extends Thread{
    @Override
    public void run() {
        super.run();
    }
}

线程调用 start() 方法之后,操作系统的任务调度器就会对线程进行调度,线程进入就绪状态,然后等待分配 CPU 时间片,如果线程分配到了 CPU 时间片,操作系统底层就会执行 run() 方法

MyThread 类继承自 Thread 类,同时重写了 run 方法,对于线程来说,如果它分配到了 CPU 时间片,操作系统底层最终调用 Thread 类的 run() 方法(可以简单的理解为运行状态下的线程只认 run() 方法),而 MyThread 重写了 run 方法,根据多态规则,最终调用的是 MyThread 类中的 run() 方法

如果多写一个 MyThread 类不够优雅,那么可以使用匿名内部类的方式

@Slf4j
public class Client {
    public static void main(String[] args) {
        Thread t1 = new Thread() {
            @Override
            public void run() {
                log.info("t1 start...");
            }
        };
    }
}

案例代码二

@Slf4j
public class Client {
    public static void main(String[] args) {
        Runnable runnable = ()->{
            log.info("t1 start...");
        };

        Thread t1 = new Thread(runnable, "t1");
        t1.start();
    }
}

使用 Runnable + Thread 方式实现的多线程,run() 方法是如何被调用的呢

我们跟踪 new Thread(runnable,"t1") 这段代码

public Thread(Runnable target, String name) {
	init(null, target, name, 0);
}

private void init(ThreadGroup g, Runnable target, String name,long stackSize) {
	init(g, target, name, stackSize, null, true);
}

private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {
	if (name == null) {
		throw new NullPointerException("name cannot be null");
	}

	this.name = name;
	Thread parent = currentThread();
	SecurityManager security = System.getSecurityManager();
	if (g == null) {
		if (security != null) {
			g = security.getThreadGroup();
		}
		if (g == null) {
			g = parent.getThreadGroup();
		}
	}
	g.checkAccess();
	if (security != null) {
		if (isCCLOverridden(getClass())) {
			security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
		}
	}

	g.addUnstarted();

	this.group = g;
	this.daemon = parent.isDaemon();
	this.priority = parent.getPriority();
	if (security == null || isCCLOverridden(parent.getClass()))
		this.contextClassLoader = parent.getContextClassLoader();
	else
		this.contextClassLoader = parent.contextClassLoader;
	this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext();
	
	// 将传进来的 Runnable 实现类对象赋值给成员变量 target		
	this.target = target;
	setPriority(priority);
	if (inheritThreadLocals && parent.inheritableThreadLocals != null)
		this.inheritableThreadLocals =
			ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
	this.stackSize = stackSize;
	tid = nextThreadID();
}

通过上面的源码得知,我们使用 Thread 带参构造方法传递进来的 Runnable 接口的实现类对象最终赋值给了 Thread 类的成员变量 Runnable target

当线程调用 start() 方法会进入到就绪状态,紧接着获取到 CPU 时间片之后会进入运行状态,运行状态的线程只会去调度当前线程类的 run() 方法

上面案例中当前线程类是 Thread 类的一个实例,找到 Thread run 方法

@Override
public void run() {
	// 如果 Runnable 类型的成员变量不为 null,则调用成员变量的 run() 方法
	if (target != null) {
		target.run();
	}
}

而通过上面我们已经知道了成员变量的 target 就是我们传递进来的 runnable,所以最终调用的是 runnable 对象的 run 方法

案例代码三

@Slf4j
public class Client {
    public static void main(String[] args) {
        FutureTask<String> futureTask = new FutureTask(()->{
            log.info("t1 start...");
            return "hello xiao mao mao!";
        });

        Thread t1 = new Thread(futureTask, "t1");
        t1.start();
    }
}

在跟代码之前先看一下 FutureTask 的继承体系吧

通过整个体系可以看出 FutureTask 也是 Runnable 接口的一个具体实现类

通过案例二可以得知使用 Thread t1 = new Thread(futureTask, "t1") 这种方式创建线程对象的时候,如果线程进入运行状态,实际上调用的是 Runnable 实现类的 run() 方法,而我们这里的 FutureTask 就是 Runnable 接口的实现类,所以运行状态的线程最终调用的是 futureTask 的 run() 方法

接着看一下 FutureTask 类的 run() 方法是怎么实现的

public void run() {
	if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread()))
		return;
	try {
		// 将成员变量 callable 赋值给变量 c
		Callable<V> c = callable;
		if (c != null && state == NEW) {
			V result;
			boolean ran;
			try {
				// 调用 Callable 实现类对象的 call 方法
				result = c.call();
				ran = true;
			} catch (Throwable ex) {
				result = null;
				ran = false;
				setException(ex);
			}
			if (ran)
				set(result);
		}
	} finally {
		runner = null;
		int s = state;
		if (s >= INTERRUPTING)
			handlePossibleCancellationInterrupt(s);
	}
}

从源码可以看出在 futureTask 对象的 run() 方法中实际上是 Callable 接口实现类的 call() 方法在执行

所以 Callable + FutureTask + Thread 这种方式下,当线程处于运行状态下时,真正执行业务逻辑的是 Callable 实现类的 call() 方法,该方法是对 run() 方法的一个增强,call() 方法可以抛出异常,也可以有返回值,而这些特性 run() 方法都不支持

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

  

 

posted @ 2023-01-05 22:10  变体精灵  阅读(137)  评论(0编辑  收藏  举报