Java多线程-线程创建方式

线程创建方式

  • 继承Thread
  • 实现Runable
  • 实现Callable

Thread

Thread是普通类,实现多线程的方式是继承该类,并重写run()方法,通过start()方法启动线程。

Runable

Runable是接口,实现多线程的方式是实现该接口,重写run()方法,并将该实现类对象传入Thread对象,通过Thread对象的start()方法启动线程。

run()方法体就是线程的执行体。

Thread和Runnable的关系

  • Thread实现了Runable接口,在Runable的基础上提供了更加丰富的属性和方法(如namestatepriorityyield()sleep()join()等)。

  • 继承Thread的方式需要重写其run()方法,即子类对象中的run()方法为线程执行体,调用start(),底层会自动调用run()方法执行线程体;

  • 实现了Runable接口的方式是重写其run()方法,并传入Thread对象,这种方法依旧是调用的Thread类对象的run()方法。那么又是如何执行Runable实现类的run()方法呢?可以看下下面Thead类的相关源码:

    /* What will be run. */
    private Runnable target;
    /**
     * Allocates a new {@code Thread} object. This constructor has the same
     * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
     * {@code (null, target, gname)}, where {@code gname} is a newly generated
     * name. Automatically generated names are of the form
     * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
     *
     * @param  target
     *         the object whose {@code run} method is invoked when this thread
     *         is started. If {@code null}, this classes {@code run} method does
     *         nothing.
     */
    public Thread(Runnable target) {
        this(null, target, "Thread-" + nextThreadNum(), 0);
    }
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

可以看到首先判断Runable实现类对象是否存在,然后执行Runable实现类对象中的run()方法.

通过Thread继承实现的多线程,每new一个线程类都会有自己的对象空间。因此不同的线程类操作的对象属性是独立的。

而通过Runable接口实现的类,可以将一个Runable实现对象传入多个Thread对象,这样每个线程操作的Runable实现类的对象属性是同一个,但此时就会出现并发性问题。

看似Runable更容易实现多线程之间的资源共享,而Thread不可以,是错误的,只是创建多线程个数不同的问题。如果使用不同的Thread对象运行不同的Runable实现也会出现内存独立的现象。如果要实现Thread的资源共享,将Thread属性设置成static即可。所以Thread和Runable没有根本的区别,只是实现方式不同罢了。

Callable

Callable是一个函数型接口,源码如下:

@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

其中call方法就是执行的线程体,可见,这种方式与以上两种线程方式不同:

  • call()方法可以声明抛出异常

  • call()方法可以有返回值

下面是Callable实现多线程的方式:

public class FutureDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<String> myCallable = new MyCallable();
        FutureTask<String> futureTask = new FutureTask<String>(myCallable);
        new Thread(futureTask).start();
        System.out.println("主线程");
    }
}

class MyCallable implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("执行call()");
        Thread.sleep(5000);
        return "执行call()结束";
    }
}

实现步骤:

1.实现Callable接口,在call()方法中编写线程执行体;

2.用Callable实现类似对象例化FutureTask类,并指定线程执行结束返回值类型;

3.用FutureTask对象实例化Thread;

4.启动线程;

FutureTask

FutureTask是一个可取消的异步计算。可以调用方法去开始和取消一个计算,可以查询计算是否完成并且获取计算结果。

FutureTask实现了Runnable和Future接口,所以FutureTask可以像Runable一样创建一个线程。

我们可以通过下面的调用实现获取Callable中的call方法体的运算结果:

	futureTask.get()

我们可以分析一下源码,首先,执行线程依旧是调用重写于Runable中的run()方法。下面是FutureTask中的run()方法:

    public void run() {
        if (state != NEW ||
            !RUNNER.compareAndSet(this, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

判断当前线程处于新建状态,执行callable对象类对象的call()方法,即执行线程体。最后执行set(result)
现在我们来看一下FuturTask的set()方法。

    protected void set(V v) {
        if (STATE.compareAndSet(this, NEW, COMPLETING)) {
            outcome = v;
            STATE.setRelease(this, NORMAL); // final state
            finishCompletion();
        }
    }

set()方法中,线程体运行结果赋值给了属性outcome
下面是get()方法和返回的report()方法结果:

	/**
	 * @throws CancellationException {@inheritDoc}
	 */
	public V get() throws InterruptedException, ExecutionException {
		int s = state;
		if (s <= COMPLETING)
			s = awaitDone(false, 0L);
		return report(s);
	}
	/**
	 * Returns result or throws exception for completed task.
	 *
	 * @param s completed state value
	 */
	@SuppressWarnings("unchecked")
	private V report(int s) throws ExecutionException {
		Object x = outcome;
		if (s == NORMAL)
			return (V)x;
		if (s >= CANCELLED)
			throw new CancellationException();
		throw new ExecutionException((Throwable)x);
	}

最后,在线程运行状态正常的情况下就返回了Callable的call方法的运算结果。

需要注意的是,futureTask.get()的调用是阻塞式的,也就是说在线程未正常结束,主线程会一直等待其执行完毕,从而获得返回结果。

我们可以从get方法中得出此结论,s <= COMPLETING,这里首先会进行一个线程状态的判断,如果线程还未执行结束,则将执行awaitDone()方法

	/**
	* Awaits completion or aborts on interrupt or timeout.
	*
	* @param timed true if use timed waits
	* @param nanos time to wait, if timed
	* @return state upon completion or at timeout
	*/
	private int awaitDone(boolean timed, long nanos)
	}

该方法注释已说明:等待完成或在中断或超时时中止。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值