精华内容
下载资源
问答
  •  位于java.lang包下的Thread类是非常重要的线程类,它实现了Runnable接口,今天我们来学习一下Thread类,在学习Thread类之前,先介绍与线程相关知识:线程的几种状态、上下文切换,然后接着介绍Thread类的方法的...

    目录

    一:Java线程介绍

    二:Java线程入口分析

    三:Java线程的创建

    四:总结


    周末抽了点时间,研究了下HotSpot是如何创建Java线程的,顺便总结一下。文中引用的源码里删除很多细节,只保留了主要过程,感兴趣的同学可以自己去深入研究。能力有限,错误地方恳请指正。

    一:Java线程介绍

    做Java开发或者Android的同学,肯定对Java线程很熟悉。之前在阅读一些JVM相关的书时,了解到Java线程其实是映射到操作系统的内核线程上的,所以Java线程基本上也就是操作系统在进行管理。在Linux中,线程和进程用的是同一个结构体进行描述的,只不过进程拥有自己独立的地址空间,而同一个进程的线程之间是共享资源的。

    二:Java线程入口分析

    想要启动一个Java线程,主要有两种方法,第一种可以实现一个继承自Thread的子类,重写run();第二种可以实现一个Runnable,交给Thread执行。这两种方法都很简便,我们可以根据自己的业务需求选择,但是无论选择哪种方法,都需要调用Thread.start(),才能真正的启动一个异步线程进行工作,笔者刚上大学接触Java时,曾经天真的以为调用Thread.run()就行了,其实直接调用run(),JVM并不会去创建一个线程,run()只会工作在原有线程,和调用普通对象的方法没有任何区别。

    上面提到了要想启动线程,必须要调用Thread.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 */
                }
            }
        }
    
        private native void start0();
    
    

    通过上面的代码可以得知,start()方法中做了一些线程状态判断等工作,但是真正启动Java线程的地方是调用了start0(),start0()是一个Native方法。start0()是何处实现的呢?我们先来看看Thread.c 中的一段定义:

    static JNINativeMethod methods[] = {
        {"start0",           "()V",        (void *)&JVM_StartThread},
        {"stop0",            "(" OBJ ")V", (void *)&JVM_StopThread},
        {"isAlive",          "()Z",        (void *)&JVM_IsThreadAlive},
        {"suspend0",         "()V",        (void *)&JVM_SuspendThread},
        {"resume0",          "()V",        (void *)&JVM_ResumeThread},
        {"setPriority0",     "(I)V",       (void *)&JVM_SetThreadPriority},
        {"yield",            "()V",        (void *)&JVM_Yield},
        {"sleep",            "(J)V",       (void *)&JVM_Sleep},
        {"currentThread",    "()" THD,     (void *)&JVM_CurrentThread},
        {"countStackFrames", "()I",        (void *)&JVM_CountStackFrames},
        {"interrupt0",       "()V",        (void *)&JVM_Interrupt},
        {"isInterrupted",    "(Z)Z",       (void *)&JVM_IsInterrupted},
        {"holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock},
        {"getThreads",        "()[" THD,   (void *)&JVM_GetAllThreads},
        {"dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
        {"setNativeName",    "(" STR ")V", (void *)&JVM_SetNativeThreadName},
    };
    
    

    上面定义了一个数组,数组中存放的为JNINativeMethod类型的结构体变量,JNINativeMethod定义在jni.h中:

    typedef struct {
        char *name;
        char *signature;
        void *fnPtr;
    } JNINativeMethod;
    
    

    JNINativeMethod主要是进行一个jni方法的映射关系,将native方法和真正的实现方法进行绑定。那么具体是何时进行绑定的呢?java层的Thread在类的静态初始化块中,调用了registerNatives()方法:

       private static native void registerNatives();
        static {
            registerNatives();
        }
    
    

    我们来看看registerNatives()对应的Jni方法:

    JNIEXPORT void JNICALL Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
    {
        (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
    }
    
    

    可见此时会将上述数组methods中的方法映射关系进行注册。注册好了后,我们再来看看上面定义的数组,这里面将Java线程中的很多native方法与实现其功能的方法指针进行了绑定,比如start0()就和JVM_StartThread进行了绑定。所以我们想研究start0(),就直接看看JVM_StartThread指针所指向的方法即可。

    三:Java线程的创建

    JVM_StartThread定义在jvm.cpp中:

    JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
      JVMWrapper("JVM_StartThread");
      JavaThread *native_thread = NULL;
      bool throw_illegal_thread_state = false;
    
      {
    
        MutexLocker mu(Threads_lock);
        if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
          throw_illegal_thread_state = true;
        } else {
          jlong size = java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
          size_t sz = size > 0 ? (size_t) size : 0;
          native_thread = new JavaThread(&thread_entry, sz);
          if (native_thread->osthread() != NULL) {
            native_thread->prepare(jthread);
          }
        }
      }
    
      ......
      Thread::start(native_thread);
    
    JVM_END
    
    

    源码中本来有很多英文注释,我这里先给删了,下面来一步步分析下上面的代码吧。

    1:判断Java线程是否已经启动,如果已经启动过,则会抛异常。

    if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
          throw_illegal_thread_state = true;
     }
    
    

    2: 如果第一步判断中,Java线程没有启动过,则会开始创建Java线程。

    jlong size = java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
    size_t sz = size > 0 ? (size_t) size : 0;
    native_thread = new JavaThread(&thread_entry, sz);
    
    

    Java线程的创建过程主要就在JavaThread的构造函数中:

    JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
                           Thread()
    {
      initialize();
      _jni_attach_state = _not_attaching_via_jni;
      set_entry_point(entry_point);
      os::ThreadType thr_type = os::java_thread;
      thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :
                                                         os::java_thread;
      os::create_thread(this, thr_type, stack_sz);
    }
    
    

    最后一句os::create_thread(this, thr_type, stack_sz) 便开始真正的创建Java线程对应的内核线程。

    bool os::create_thread(Thread* thread, ThreadType thr_type,size_t req_stack_size) {
        ......
        pthread_t tid;
        int ret = pthread_create(&tid, &attr, (void* (*)(void*)) thread_native_entry, thread);
        ......
        return true;
    }
    
    

    上面这个方法主要就是利用pthread_create()来创建线程。其中第三个参数thread_native_entry便是新起的线程运行的初始地址,其为定义在os_bsd.cpp中的一个方法指针,而第四个参数thread即thread_native_entry的参数:

    static void *thread_native_entry(Thread *thread) {
      ......
      thread->run();
      ......
      return 0;
    }
    
    

    新线程创建后就会从thread_native_entry()开始运行,thread_native_entry()中调用了thread->run():

    // thread.cpp
    void JavaThread::run() {
      ......
      thread_main_inner();
    }
    
    

    此方法最后调用了thread_main_inner():

    // thread.cpp
    void JavaThread::thread_main_inner() {
      if (!this->has_pending_exception() &&
          !java_lang_Thread::is_stillborn(this->threadObj())) {
        {
          ResourceMark rm(this);
          this->set_native_thread_name(this->get_thread_name());
        }
        HandleMark hm(this);
        this->entry_point()(this, this);
      }
      DTRACE_THREAD_PROBE(stop, this);
      this->exit(false);
      delete this;
    }
    
    

    我们重点关注下this->entry_point()(this, this),entry_point()返回的其实就是在 new JavaThread(&thread_entry, sz) 时传入的thread_entry。这里就相当于调用了thread_entry(this,this)。thread_entry定义在jvm.cpp中:

    // jvm.cpp
    static void thread_entry(JavaThread* thread, TRAPS) {
      HandleMark hm(THREAD);
      Handle obj(THREAD, thread->threadObj());
      JavaValue result(T_VOID);
      JavaCalls::call_virtual(&result,
                              obj,
                              KlassHandle(THREAD, SystemDictionary::Thread_klass()),
                              vmSymbols::run_method_name(),
                              vmSymbols::void_method_signature(),
                              THREAD);
    }
    
    

    哈哈,这里见到了一个老朋友:JavaCalls。JavaCalls模块是用来调用Java方法的。不了解的朋友可以直接看之前写的文章《JVM方法执行的来龙去脉》。我们来看看这里调用JavaCalls::call_virtual()传入的一些参数:

    • obj:Java线程对象;
    • KlassHandle(THREAD, SystemDictionary::Thread_klass()):Java线程类,记录在SystemDictionary中,即java_lang_Thread;
    • vmSymbols::run_method_name():即"run";
    • vmSymbols::void_method_signature():即"()V";

    经过上面的分析,这里其实就是开始调用Java线程对象的run()方法。

    3:开始执行所创建的内核线程,即从第二步所说的thread_entry处开始执行。

    Thread::start(native_thread);
    
    

    四:总结

    到这里Java线程就已经真正的运行起来了,总结下上面的过程:

    • 1:调用Java线程start()方法,通过jni方式,调用到JVM层。
    • 2:JVM通过pthread_create()创建一个系统内核线程,并指定内核线程的初始运行地址,即一个方法指针。
    • 3:在内核线程的初始运行方法中,利用JavaCalls模块,调用java线程的run()方法,开始java级别的线程执行。

    推荐阅读:

    Java编程思想之多线程(三)

    作者:半栈工程师
    链接:https://www.jianshu.com/p/0dd2c5e2d63b
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    展开全文
  • 首先我们来看下老师给我们讲过创建线程“两种”方法:一、继承Thread类创建线程子类1.在这子类中重写run方法,在run方法内写线程任务代码2.创建该子类实例,即创建了一个线程实例3.调用该实例start方法来...

    首先我们来看下老师给我们讲过的创建线程的“两种”方法:

    一、继承Thread类创建线程子类

    1.在这子类中重写run方法,在run方法内写线程任务代码

    2.创建该子类实例,即是创建了一个线程实例

    3.调用该实例的start方法来启动该线程

    二、建一个类去实现Runnable接口

    1.该类去实现接口的run方法,run方法内写线程任务代码

    2.创建该类实例,把该实例当作一个标记target传给Thread类,如:Thread t = new

    Thread(该类实例);即创建一个线程对象

    3.调用线程的star方法来启用该线程

    扩展一下第三种老师课上没讲过的创建线程方法:

    三、通过Callable接口和Future创建线程

    1.创建一个类去实现Callable接口,实现该接口的call方法

    CallableTest implements Callable{

    public Integer call(){}

    }

    2.创建实现Callable接口的类的实例,用FutureTask类来包装该对象

    CallableTest ct = new CallableTest();

    FutureTask ft = new FutureTask(ct);

    3.用FutureTask对象作为Thread对象的target创建并启动新线程

    Thread t = new Thread(ft);

    t.start();

    优劣:

    二、三两种方式是实现某接口,可以去继承其他类,操作相对灵活,并且能多个纯种共享一个对象Thread t = new

    Thread(ft);里面的ft对象能多个线程共享,劣势是编程相对复杂

    一种方式是继承Thread类,不能再继承其他类,编程相对简单

    展开全文
  • java.lang.Thread类详解一、前言位于java.lang包下的Thread类是非常重要线程类,它实现了Runnable接口,今天我们来学习一下Thread类,在学习Thread类之前,先介绍与线程相关知识:线程几种状态、上下文切换,...

    java.lang.Thread类详解

    一、前言

    位于java.lang包下的Thread类是非常重要的线程类,它实现了Runnable接口,今天我们来学习一下Thread类,在学习Thread类之前,先介绍与线程相关知识:线程的几种状态、上下文切换,然后接着介绍Thread类中的方法的具体使用。

    二、线程的状态

    在正式学习Thread类中的具体方法之前,我们先来了解一下线程有哪些状态,这个将会有助于后面对Thread类中的方法的理解。

    线程从创建到最终的消亡,要经历若干个状态。一般来说,线程包括以下这几个状态:创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)。

    当需要新起一个线程来执行某个子任务时,就创建了一个线程。但是线程创建之后,不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源,譬如程序计数器、Java栈、本地方法栈都是线程私有的,所以需要为线程分配一定的内存空间),只有线程运行需要的所有条件满足了,才进入就绪状态。

    当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。

    线程在运行状态过程中,可能有多个原因导致当前线程不继续运行下去,比如用户主动让线程睡眠(睡眠一定的时间之后再重新执行)、用户主动让线程等待,或者被同步块给阻塞,此时就对应着多个状态:time waiting(睡眠或等待一定的事件)、waiting(等待被唤醒)、blocked(阻塞)。

    当由于突然中断或者子任务执行完毕,线程就会被消亡。

    下面这副图描述了线程从创建到消亡之间的状态:

    ed67c49ef918d6ede446ee8e16870655.png

    在有些教程上将blocked、waiting、time waiting统称为阻塞状态,这个也是可以的,只不过这里我想将线程的状态和Java中的方法调用联系起来,所以将waiting和time waiting两个状态分离出来。

    三、上下文切换

    对于单核CPU来说(对于多核CPU,此处就理解为一个核),CPU在一个时刻只能运行一个线程,当在运行一个线程的过程中转去运行另外一个线程,这个叫做线程上下文切换(对于进程也是类似)。

    由于可能当前线程的任务并没有执行完毕,所以在切换时需要保存线程的运行状态,以便下次重新切换回来时能够继续切换之前的状态运行。举个简单的例子:比如一个线程A正在读取一个文件的内容,正读到文件的一半,此时需要暂停线程A,转去执行线程B,当再次切换回来执行线程A的时候,我们不希望线程A又从文件的开头来读取。

    因此需要记录线程A的运行状态,那么会记录哪些数据呢?因为下次恢复时需要知道在这之前当前线程已经执行到哪条指令了,所以需要记录程序计数器的值,另外比如说线程正在进行某个计算的时候被挂起了,那么下次继续执行的时候需要知道之前挂起时变量的值时多少,因此需要记录CPU寄存器的状态。所以一般来说,线程上下文切换过程中会记录程序计数器、CPU寄存器状态等数据。

    说简单点的:对于线程的上下文切换实际上就是 存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。

    虽然多线程可以使得任务执行的效率得到提升,但是由于在线程切换时同样会带来一定的开销代价,并且多个线程会导致系统资源占用的增加,所以在进行多线程编程时要注意这些因素。

    四、Thread类中的方法

    通过查看java.lang.Thread类的源码可知:

    c44ea3aa37fa6c6969a5644b327a1561.png

    Thread类实现了Runnable接口,在Thread类中,有一些比较关键的属性,比如name是表示Thread的名字,可以通过Thread类的构造器中的参数来指定线程名字,priority表示线程的优先级(最大值为10,最小值为1,默认值为5),daemon表示线程是否是守护线程,target表示要执行的任务。

    下面是Thread类中常用的方法:

    以下是关系到线程运行状态的几个方法:

    1)start方法

    start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。

    2)run方法

    run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。

    3)sleep方法

    sleep方法有两个重载版本:

    1

    2

    sleep(long millis)//参数为毫秒

    sleep(long millis,int nanoseconds)//第一参数为毫秒,第二个参数为纳秒

    sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。

    如果需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread类的静态sleep()方法来实现。

    当当前线程调用sleep()方法进入阻塞状态后,在其睡眠时间内,该线程不会获得执行机会,即使系统中没有其他可执行线程,处于sleep()中的线程也不会执行,因此sleep()方法常用来暂停程序的执行

    但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。看下面这个例子就清楚了:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    public class Test {

    private int i =10;

    private Object object =new Object();

    public static void main(String[] args)throwsIOException  {

    Test test =new Test();

    MyThread thread1 = test.new MyThread();

    MyThread thread2 = test.new MyThread();

    thread1.start();

    thread2.start();

    }

    class MyThreadextends Thread{

    @Override

    public void run() {

    synchronized (object) {

    i++;

    System.out.println("i:"+i);

    try {

    System.out.println("线程"+Thread.currentThread().getName()+"进入睡眠状态");

    Thread.currentThread().sleep(10000);

    }catch(InterruptedException e) {

    // TODO: handle exception

    }

    System.out.println("线程"+Thread.currentThread().getName()+"睡眠结束");

    i++;

    System.out.println("i:"+i);

    }

    }

    }

    }

    输出结果:

    486249076b27c506ef1ffa5204787f18.png

    从上面输出结果可以看出,当Thread-0进入睡眠状态之后,Thread-1并没有去执行具体的任务。只有当Thread-0执行完之后,此时Thread-0释放了对象锁,Thread-1才开始执行。

    注意,如果调用了sleep方法,必须捕获InterruptedException异常或者将该异常向上层抛出。当线程睡眠时间满后,不一定会立即得到执行,因为此时可能CPU正在执行其他的任务。所以说调用sleep方法相当于让线程进入阻塞状态。

    4)yield方法

    yield()方法和sleep()方法有点相似,它也是Thread类提供的一个静态方法,它也可以让当前正在执行的线程暂停,但它不会阻塞该线程,它只是将该线程转入到就绪状态。即让当前线程暂停一下,让系统的线程调度器重新调度一次,完全可能的情况是:当某个线程调用了yield()方法暂停之后,线程调度器又将其调度出来重新执行。

    调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,当某个线程调用了yield()方法之后,只有优先级与当前线程相同或者比当前线程更高的处于就绪状态的线程才会获得执行机会。

    注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。

    5)join方法

    join方法有三个重载版本:

    1

    2

    3

    join()

    join(long millis)//参数为毫秒

    join(long millis,int nanoseconds)//第一参数为毫秒,第二个参数为纳秒

    假如在main线程中,调用thread.join方法,则main方法会等待thread线程执行完毕或者等待一定的时间。如果调用的是无参join方法,则等待thread执行完毕,如果调用的是指定了时间参数的join方法,则等待一定的事件。

    看下面一个例子:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    public class Test {

    public static void main(String[] args)throwsIOException  {

    System.out.println("进入线程"+Thread.currentThread().getName());

    Test test =new Test();

    MyThread thread1 = test.new MyThread();

    thread1.start();

    try {

    System.out.println("线程"+Thread.currentThread().getName()+"等待");

    thread1.join();

    System.out.println("线程"+Thread.currentThread().getName()+"继续执行");

    }catch (InterruptedException e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    }

    }

    class MyThreadextends Thread{

    @Override

    public void run() {

    System.out.println("进入线程"+Thread.currentThread().getName());

    try {

    Thread.currentThread().sleep(5000);

    }catch (InterruptedException e) {

    // TODO: handle exception

    }

    System.out.println("线程"+Thread.currentThread().getName()+"执行完毕");

    }

    }

    }

    输出结果:

    dc5410efef8cc0fa52f886f7cd8cfa84.png

    可以看出,当调用thread1.join()方法后,main线程会进入等待,然后等待thread1执行完之后再继续执行。

    实际上调用join方法是调用了Object的wait方法,这个可以通过查看源码得知:

    539137458855714488733a0c67dce938.png

    wait方法会让线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限。

    由于wait方法会让线程释放对象锁,所以join方法同样会让线程释放对一个对象持有的锁。具体的wait方法使用在后面文章中给出。

    6)interrupt方法

    interrupt,顾名思义,即中断的意思。单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,也就说,它可以用来中断一个正处于阻塞状态的线程;另外,通过interrupt方法和isInterrupted()方法来停止正在运行的线程。

    下面看一个例子:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    public class Test {

    public static void main(String[] args)throwsIOException  {

    Test test =new Test();

    MyThread thread = test.new MyThread();

    thread.start();

    try {

    Thread.currentThread().sleep(2000);

    }catch (InterruptedException e) {

    }

    thread.interrupt();

    }

    class MyThreadextends Thread{

    @Override

    public void run() {

    try {

    System.out.println("进入睡眠状态");

    Thread.currentThread().sleep(10000);

    System.out.println("睡眠完毕");

    }catch (InterruptedException e) {

    System.out.println("得到中断异常");

    }

    System.out.println("run方法执行完毕");

    }

    }

    }

    输出结果:

    2d5bef5bd19683044c94fe24b03721ab.png

    从这里可以看出,通过interrupt方法可以中断处于阻塞状态的线程。那么能不能中断处于非阻塞状态的线程呢?看下面这个例子:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    public class Test {

    public static void main(String[] args)throwsIOException  {

    Test test =new Test();

    MyThread thread = test.new MyThread();

    thread.start();

    try {

    Thread.currentThread().sleep(2000);

    }catch (InterruptedException e) {

    }

    thread.interrupt();

    }

    class MyThreadextends Thread{

    @Override

    public void run() {

    int i =0;

    while(i

    System.out.println(i+" while循环");

    i++;

    }

    }

    }

    }

    运行该程序会发现,while循环会一直运行直到变量i的值超出Integer.MAX_VALUE。所以说直接调用interrupt方法不能中断正在运行中的线程。

    但是如果配合isInterrupted()能够中断正在运行的线程,因为调用interrupt方法相当于将中断标志位置为true,那么可以通过调用isInterrupted()判断中断标志是否被置位来中断线程的执行。比如下面这段代码:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    public class Test {

    public static void main(String[] args)throwsIOException  {

    Test test =new Test();

    MyThread thread = test.new MyThread();

    thread.start();

    try {

    Thread.currentThread().sleep(2000);

    }catch (InterruptedException e) {

    }

    thread.interrupt();

    }

    class MyThreadextends Thread{

    @Override

    public void run() {

    int i =0;

    while(!isInterrupted() && i

    System.out.println(i+" while循环");

    i++;

    }

    }

    }

    }

    运行会发现,打印若干个值之后,while循环就停止打印了。

    但是一般情况下不建议通过这种方式来中断线程,一般会在MyThread类中增加一个属性 isStop来标志是否结束while循环,然后再在while循环中判断isStop的值。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    class MyThreadextends Thread{

    private volatile boolean isStop =false;

    @Override

    public void run() {

    int i =0;

    while(!isStop){

    i++;

    }

    }

    public void setStop(boolean stop){

    this.isStop = stop;

    }

    }

    那么就可以在外面通过调用setStop方法来终止while循环。

    7)interrupted方法

    interrupted()函数是Thread静态方法,用来检测当前线程的interrupt状态,检测完成后,状态清空。通过下面的interrupted源码我们能够知道,此方法首先调用isInterrupted方法,而isInterrupted方法是一个重载的native方法private native boolean isInterrupted(boolean ClearInterrupted) 通过方法的注释能够知道,用来测试线程是否已经中断,参数用来决定是否重置中断标志。

    public static boolean interrupted() {

    return currentThread().isInterrupted(true);

    }

    public boolean isInterrupted() {

    return isInterrupted(false);

    }

    /**

    * Tests if some Thread has been interrupted. The interrupted state

    * is reset or not based on the value of ClearInterrupted that is

    * passed.

    */

    private native boolean isInterrupted(boolean ClearInterrupted);

    8)stop方法

    stop方法已经是一个废弃的方法,它是一个不安全的方法。因为调用stop方法会直接终止run方法的调用,并且会抛出一个ThreadDeath错误,如果线程持有某个对象锁的话,会完全释放锁,导致对象状态不一致。所以stop方法基本是不会被用到的。

    9)destroy方法

    destroy方法也是废弃的方法。基本不会被使用到。

    以下是关系到线程属性的几个方法:

    1)getId

    用来得到线程ID

    2)getName和setName

    用来得到或者设置线程名称。

    3)getPriority和setPriority

    用来获取和设置线程优先级。

    4)setDaemon和isDaemon

    用来设置线程是否成为守护线程和判断线程是否是守护线程。

    守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。

    Thread类有一个比较常用的静态方法currentThread()用来获取当前线程。

    在上面已经说到了Thread类中的大部分方法,那么Thread类中的方法调用到底会引起线程状态发生怎样的变化呢?下面一幅图就是在上面的图上进行改进而来的:

    77e65e17aec7aee59a6427973ab25e96.png

    参考文章:

    展开全文
  • 分别继承Thread类、实现Runnable接口、实现Callable接口,然后分别实现对应的方法,这里不过多描述。 那么如果采用继承Thread类方式,匿名类方式实现Runnable对应run()方法后,有几种启动线程的方式,各有什么...

    大家都知道JAVA中开启线程最基础的方法有三种。分别是继承Thread类、实现Runnable接口、实现Callable接口,然后分别实现对应的方法,这里不过多描述。

    那么如果采用继承Thread类方式,匿名类方式实现Runnable对应run()方法后,有几种启动线程的方式,各有什么区别?

    package com.algorithm;
    
    /**
     * 面试锦集:
     * Thread启动线程有哪些方法,区别是什么
     *
     * @author 小辉哥/小辉GE
     * <p>
     * 2019年8月4日 上午9:22:12
     */
    
    import java.util.concurrent.TimeUnit;
    
    public class ThreadStartRunDifference {
    
    	public static void main(String[] args) {
    
    		Thread thread = new Thread() {
    			@Override
    			public void run() {
    				try {
    					//为了更好的体现输出结果, 让线程sleep 5秒
    					TimeUnit.SECONDS.sleep(5);
    					System.out.println(Thread.currentThread().getName() + "线程输出了 ......");
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    
    			}
    		};
    		
    		// 总结:
    		// Thread的run()和start()都可以启动线程
    		// 区别是run()方法直接是主线程main调用,需要等待线程运行完毕。 start()方法是开启一个线程执行
    		
    		//thread.run();
    		thread.start();
    		System.out.println("main方法执行完毕了......");
    	}
    
    }
    

    首先采用start()方法调用,测试输出结果如下:

    然后采用run()方法调用,测试输出结果如下:

    在这里我们稍微做一下总结:

    Thread的run()和start()都可以启动线程,区别是run()方法是主线程main直接调用,需要等待线程运行完毕。 start()方法是开启一个异步线程执行!

    以上代码仅供参考,如有不当之处,欢迎指出!!!

    更多干货,欢迎大家关注和联系我。期待和大家一起更好的交流、探讨技术!!!

     

     

    展开全文
  • Thread类的使用

    2016-06-02 20:10:16
     三.Thread类的方法  若有不正之处,请多多谅解并欢迎批评指正。  请尊重作者劳动成果,转载请标明原文链接:  http://www.cnblogs.com/dolphin0520/p/3920357.html 一.线程的状态  在正式学习Thread类...
  • java多线程实现方式有4种,分别继承Thread类、实现Runnable接口、实现Callable接口,从线程池中获取,其中前两种比较常用。一下就是线程创建详细步骤以及注意事项:1.继承Thread类说明:调用start方法后并不...
  • thread 详解

    2019-06-17 17:14:00
     位于java.lang包下的Thread类是非常重要的线程类,它实现了Runnable接口,今天我们来学习一下Thread类,在学习Thread类之前,先介绍与线程相关知识:线程的几种状态、上下文切换,然后接着介绍Thread类的方法的...
  • 今天我们来学习一下Thread类,在学习Thread类之前,先介绍与线程相关...线程的状态 在正式学习Thread类中的具体方法之前,我们先来了解一下线程有哪些状态,这个将会有助于后面对Thread类的方法的理解。 线程从创
  • java.lang.Thread类详解一、前言位于java.lang包下的Thread类是非常重要线程类,它实现了Runnable接口,今天我们来学习一下Thread类,在学习Thread类之前,先介绍与线程相关知识:线程几种状态、上下文切换,...
  • 跟汤老师学Java笔记:创建线程方式一继承Thread类 完成:第一遍 1.、创建线程有哪两种方式? 两种方式: 继承Thread类 实现Runnable接口 2.如何通过继承Thread类来创建线程? 步骤: (1). 定义一个类,继承自...
  • 1. 下面哪些是Thread类的方法?A start() B run() C exit() D getPriority()答案:ABD解析:看 Java API docs吧:http://docs.oracle.com/javase/7/docs/api/, exit() 是 System 类的方法,如System.exit(0)。2. ...
  • 前几篇都介绍了Thread中几个方法,相信大家都发现一个相似点,那就是sleep,join,wait这样阻塞方法都必须捕获一个InterruptedException...在java线程Thread类中有三个方法,比较容易混淆,在这里解释一下 (...
  • 1.继承Thread类,则具备的线程的所有特性,其缺点影响了该类再继承别的类了,即java类的单继承性。 2.如果runnable,则具备了线程的执行能力,但必须架接到Thread类中才可以真正执行线程。其优点可以让该类再...
  • 点击蓝色“程序职场”关注我哟加个“星标”,天天和你一起进步作者:Only MI 链接:https://blog.csdn.net/weixin_44664432/article/details/106711074方法一 创建Thread类的子类java.lang.Thread类,描述线程的类...
  • 以下本文的目录大纲:  一.线程的状态  二.... 三.Thread类的方法 ...一.... 在正式学习Thread类中的具体方法之前,我们先来了解一下线程有哪些状态,这个将会有助于后面对Thread类的方法
  • 答:以下几点:sleep是Thread类的方法,wait是Object类的方法。sleep可以用在任意方法中,wait只能用在同步方法或同步块中。 sleep是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用b的sleep方法,实际上...
  • javalang包中常用哪些

    千次阅读 2009-09-15 12:24:00
    1 String,八种基本数据类型包装都在这个包中2 Object:层次结构3 Math:执行基本数学方法运算4 StringBuffer:线程安全可变字符序列5 thread程序中执行线程。Java 虚拟机允许应用程序并发地...
  • 基本的差别sleep是Thread类的方法,wait是Object类的方法sleep方法可以在任何地方使用,wait只能在synchronized方法或synchronized块中使用最主要的本质区别Thread.sleep只会让出CPU,不会释放对象锁Object.wait不仅...
  • Thread类和Runnable接口

    2011-12-08 20:04:00
    两者都可以实现多线程,那么两者的区别和联系...从Thread类的定义可以清楚的发现,Thread类也Runnable接口的子类,但是Thread类中并没有完全地实现Runnable接口中的run()方法, 区别:如果一个类继承Thread类,...
  • 我们知道在Java里线程通过java.lang.Thread类来实现。一般我们创建无返回值线程会用下面两个方法: 继承Thread类,重写run()方法; 实现Runnable接口,重写run()方法; 线程启动会通过调用start方法来启动...
  • 总觉得对Thread类的使用有些许的迷惑,好像总是隔着一层纱,使用Thread类的方法时会畏手畏脚,近期总算有时间把Thread类的源码看了一遍,瞬间感觉那层纱就被揭开了 对源码的解析按照线程的域、常用方法、涉及的相关...
  • 下面哪些是Thread类的方法?A.Start() B.run()方法 C.exit()方法 D. getPriority()方法解析:exit()方法 是 System类的方法2.GC线程是否为守护线程?答:是解析:线程分为(1)守护线程(2)非守护线程只要当前jvm尚存...
  • Java并发--Thread类详情

    2017-10-11 09:00:00
    以下本文的目录大纲:  一.线程的状态 ... 在正式学习Thread类中的具体方法之前,我们先来了解一下线程有哪些状态,这个将会有助于后面对Thread类的方法的理解。  线程从创建到最终的...
  • 答:有两种实现方法,分别继承Thread类与实现Runnable接口用synchronized关键字修饰同步方法反对使用stop(),因为它不安全。它会解除由线程获取所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在...
  • 前几篇都介绍了Thread中几个方法,相信大家都发现一个相似点,那就是sleep,join,wait这样阻塞方法都必须捕获一个...在Java线程Thread类中有三个方法,比较容易混淆,在这里解释一下 (1)inte
  • 创建线程两种方法,从 Thread 中更多相关问题[填空题] 馈线连接头都必须牢固安装,接触良好,室外馈线连接头以及室内馈线连接头放置于水管井内,应做()。[填空题] UE随机接入目的()。[填空题] 馈线...
  • 【其它】程序设计实验一.pptx【其它】WORD操作一: 样式排版要求.docx 样式...【单选题】卡耐基说:“一个人成功,只有____由于他专业技术,而____则要靠人际关系和他做人处事能力。”【判断题】处于等待线程,...

空空如也

空空如也

1 2 3 4 5 ... 18
收藏数 357
精华内容 142
关键字:

哪些是thread类的方法