死磕Java线程—Thread

深入了解Thread常用方法。

这篇博客呢没什么技术含量,本来Thread源码并不在我最近的学习范围内,只是发现线程池的源码看太不懂(毕竟还没怎么用过),所以想着先看看简单点的Thread,在这里呢只列举出了Thread中常用的方法源码。

在正式看源码之前,先来看一段小程序吧

interface MyRunnable{
	void run();
}

class MyThread implements MyRunnable{
	private MyRunnable target;
	@Override
	public void run() {
		if(target!=null) 
			target.run();
	}
	public void start() {
		this.run();
	}
	public MyThread(MyRunnable target) {
		this.target=target;
	}
}

public class Run implements MyRunnable{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("Run");
	}
	public static void main(String[] args) {
		MyThread myThread=new MyThread(new Run());
		myThread.start();
	}
}

程序分析:用过Thread就知道,上面这段程序的目的是:模仿Thread调用start()方法。

没错,任何一个Java程序员都会告诉你:创建一个新线程,我们只需要实现Runnable接口,然后实现该接口要求实现的方法run(),最后由Thread实例调用start()方法就可以了。只需要知道在start()方法内部会对run()方法进行调用。事实也是这样。

源码分析

1、start()

public synchronized void start() {
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

源码分析:

当调用start()时,首先会判断threadStatus—线程状态,防止重复启动同一线程

/*
     * Java thread status for tools, default indicates thread 'not yet started'
     */
private volatile int threadStatus;

根据源码注释,该值用于记录线程是否已经被启动。0表示没有启动。默认为0。若判断到threadStatus不等于0,则抛出异常IllegalThreadStateException

接下来调用group.add(this);将该线程添加到队列中,group是一个ThreadGroup对象

void add(Thread t) {
        synchronized (this) {
            if (destroyed) {
                throw new IllegalThreadStateException();
            }
            if (threads == null) {
                threads = new Thread[4];
            } else if (nthreads == threads.length) {
                threads = Arrays.copyOf(threads, nthreads * 2);
            }
            threads[nthreads] = t;

            nthreads++;

            nUnstartedThreads--;
        }
    }

ThreadGroup的本质就是一个数组,用于存放创建好的线程数,该数组的默认大小为4,当线程数超过4时,会调用数组工具类Arrays.copyOf()来为数组扩容,一次性扩大为原来的两倍

接着用变量started来标识线程是否成功创建。调用了start0()来在本地机器中开辟一个线程,若成功创建,则started标识为true,若创建失败,将该线程从数组中移除

private native void start0();

start0()是一个本地方法,在本地机器上开辟了一个新线程的同时会调用run()方法执行对应的工作

2、run()

@Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

源码分析:

这个比较简单了,run()方法是在接口Runnable中定义的,Thread实现了Runnable

public class Thread implements Runnable {...}

target是一个Runnable引用,它的值是通过构造方法中传进来的,通过该接口对象调用其实现类的run()方法

public Thread(Runnable target) {
        this(null, target, "Thread-" + nextThreadNum(), 0);
    }

3、sleep(),yield()

public static native void sleep(long millis) throws InterruptedException;

public static native void yield();

源码分析:

这两个方法都是本地方法,调用两方法都涉及到一个对象的锁。常常和wait()一起做比较,为了更好的理解,先来看线程的状态有哪些吧。

4、获得线程的状态getState()

public enum State {

        NEW,

        RUNNABLE,

        BLOCKED,

        WAITING,

        TIMED_WAITING,

        TERMINATED;
    }

源码分析:

State是一个枚举,是Thread的内部类,用来表示线程的状态。从源码来看,线程的状态可以分为New(新创建),Runnable(可运行),Block(阻塞),Waiting(等待),Timed Waiting(计时等待),Terminated(被终止)六种

要获得一个线程的状态可以调用getState(),Thread中getState()的实现

public State getState() {
        // get current thread state
        return jdk.internal.misc.VM.toThreadState(threadStatus);
    }

继续深入到jdk.internal.misc.VM.toThreadState(threadStatus);该方法位于VM类

public static Thread.State toThreadState(int threadStatus) {
        if ((threadStatus & JVMTI_THREAD_STATE_RUNNABLE) != 0) {
            return RUNNABLE;
        } else if ((threadStatus & JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER) != 0) {
            return BLOCKED;
        } else if ((threadStatus & JVMTI_THREAD_STATE_WAITING_INDEFINITELY) != 0) {
            return WAITING;
        } else if ((threadStatus & JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT) != 0) {
            return TIMED_WAITING;
        } else if ((threadStatus & JVMTI_THREAD_STATE_TERMINATED) != 0) {
            return TERMINATED;
        } else if ((threadStatus & JVMTI_THREAD_STATE_ALIVE) == 0) {
            return NEW;
        } else {
            return RUNNABLE;
        }
    }

6、最后再来说说sleep(),yield(),wait()

  • sleep()和yield()都是Thread中的方法
  • wait()是Object中的方法
  • 通过调用sleep(milliseconds)可以使任务进入休眠状态,在这种情况下,任务在指定的时间内不会运行
  • 通过调用yield()可以告诉线程调用机制,自己已经完成了最重要的任务,接下来可以把CPU时间片段分配给其他线程了
  • 通过调用wait()可以使线程挂起。直到线程得到了notify()或notifyAll()消息,线程才进入就绪状态
  • 调用sleep()的时候,对象的锁并没有被释放,yield()也属于这种情况,意味着其他线程仍然不能获得这个锁
  • 调用wait()的时候,对象的锁会被释放,意味着另一个线程可以获得这个锁

关于sleep(),yield(),wait()与线程状态以及锁的关系,用一张图表示

图中对应了线程的六种状态,其中就绪和运行都属于Runnable状态(Java的规范说明没有将Runnable作为一个单独的状态,一个正在运行的线程仍然处于可运行状态)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值