小言_互联网的博客

初识 Java 并发

596人阅读  评论(0)

并发

1、线程的生命周期

public enum State {
        
        NEW,
    
        RUNNABLE,
    
        BLOCKED,

        WAITING,
    
        TIMED_WAITING,

        TERMINATED;
    }

Thread 类中有一枚举类 State 定义了线程的六种状态

  • NEW状态:新创建出来一个线程
  • RUNNABLE:调用 start() 方法之后,会处于此状态,等待获取时间片之后进行 RUNNING 执行 run()
  • BLOCK:当我们有两个 a,b 去调用 synchronized add() 方法时,若 a 先获得锁,b 在去获得锁时,便会返回 BLOCK 状态
  • WAITING:处于一个等待状态,调用 wait,join,LockSupport.park 方法后,会处于此状态
  • TIME_WAITING:和 WAITING 的区别就是,这个可以设置超时时间,等待超过超时时间后,会进行释放
  • TERMINATED:线程执行完毕,或者出现异常

2、线程状态的演示

// WAITING 状态
new Thread(() -> {
            while (true) {
                synchronized (ThreadStatus.class) {
                    try {
                        ThreadStatus.class.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "wait状态").start();

// TIME_WAITING 状态
new Thread(() -> {
            while (true) {
                synchronized (ThreadStatus.class) {
                    try {
                        TimeUnit.SECONDS.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "TIME_WAITING状态").start();

// BLOCK 状态
App app = new App();
new Thread(() -> app.demo()).start();
new Thread(() -> app.demo(), "BLOCK状态").start();

static class App {
        public synchronized void demo() {
            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

可以通过 jps 查看 pid,通过 jstack pid 查看jvm 相关信息

"BLOCK状态" #15 prio=5 os_prio=0 tid=0x00000000207c9000 nid=0x9ca4 waiting for monitor entry [0x000000002132f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at ThreadStatus$App.demo(ThreadStatus.java:47)
        - waiting to lock <0x000000076bebe398> (a ThreadStatus$App)
        at ThreadStatus.lambda$main$3(ThreadStatus.java:40)
        at ThreadStatus$$Lambda$4/558638686.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"TIME_WAITING状态" #13 prio=5 os_prio=0 tid=0x00000000207c4800 nid=0xa6ec waiting on condition [0x000000002112e000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at java.lang.Thread.sleep(Thread.java:340)
        at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
        at ThreadStatus.lambda$main$1(ThreadStatus.java:29)
        - locked <0x000000076bb9a968> (a java.lang.Class for ThreadStatus)
        at ThreadStatus$$Lambda$2/1078694789.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"wait状态" #12 prio=5 os_prio=0 tid=0x00000000207bf800 nid=0x56bc in Object.wait() [0x000000002102f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x000000076bb9a968> (a java.lang.Class for ThreadStatus)
        at java.lang.Object.wait(Object.java:502)
        at ThreadStatus.lambda$main$0(ThreadStatus.java:17)
        - locked <0x000000076bb9a968> (a java.lang.Class for ThreadStatus)
        at ThreadStatus$$Lambda$1/1324119927.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

3、线程是怎么启动的

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) {
              
            }
        }
    }

当我们调用 start 方法后,由源码可见,在其内部调用了 syary0() 的一个 native 方法

在 openJDK 的 Thread.c 中可以看到

​ 也就是说,start0 这个方法,调用的是 JVM_StartThread 这个方法

// JVM.CPP
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_StartThread");
  JavaThread *native_thread = NULL;

      size_t sz = size > 0 ? (size_t) size : 0;
      native_thread = new JavaThread(&thread_entry, sz);
 		... ...

  	Thread::start(native_thread);

JVM_END

JVM_ENTRY 就是定义了 JVM_StartThread 这个函数,可以发现,在代码中进行了 new JavaThread 的操作,并在之后调用了 Thread 的 start 方法,将 new 的 Thread 的传入,启动这个线程

new JavaThread 的相关操作在代码 Thread.cpp 文件下

// Thread.cpp
JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
  Thread()
#if INCLUDE_ALL_GCS
  , _satb_mark_queue(&_satb_mark_queue_set),
  _dirty_card_queue(&_dirty_card_queue_set)
#endif // INCLUDE_ALL_GCS
{
  if (TraceThreadEvents) {
    tty->print_cr("creating thread %p", this);
  }
  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;
  // 操作系统 底层创建 java_thread
   os::create_thread(this, thr_type, stack_sz);
  _safepoint_visible = false;
 	... ...
}

在注释的地方,可以看到 new JavaThread 主要是通过操作系统底层创建出来一个 Thread 线程

接着就是 start 启动,同样也是通过操作系统底层对线程进行启动

// Thread.cpp
// Thread.start()
void Thread::start(Thread* thread) {
  trace("start", thread);
  // Start is different from resume in that its safety is guaranteed by context or
  // being called from a Java method synchronized on the Thread object.
  if (!DisableStartThread) {
    if (thread->is_Java_thread()) {
      // Initialize the thread state to RUNNABLE before starting this thread.
      // Can not set it after the thread started because we do not know the
      // exact thread state at that time. It could be in MONITOR_WAIT or
      // in SLEEPING or some other state.
      java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
                                          java_lang_Thread::RUNNABLE);
    }
    // 通过 操作系统底层,启动线程
    os::start_thread(thread);
  }
}
// os_Linux.cpp
static void *java_start(Thread *thread) {
 
  static int counter = 0;
  int pid = os::current_process_id();
  alloca(((pid ^ counter++) & 7) * 128);

  ThreadLocalStorage::set_thread(thread);

  OSThread* osthread = thread->osthread();
  Monitor* sync = osthread->startThread_lock();

 ... ...

  // handshaking with parent thread
  {
    MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);

    // notify parent thread
    osthread->set_state(INITIALIZED);
    sync->notify_all();

    // wait until os::start_thread()
    while (osthread->get_state() == INITIALIZED) {
      sync->wait(Mutex::_no_safepoint_check_flag);
    }
  }

  // 进行回调 run() 方法
  thread->run();

  return 0;
}

由以上也就可以得出为什么线程启动的方法,不是调用 run 方法,而是调用 start 方法,去启动线程,同时,在底层代码中,会对 run 方法进行回调 ,这也就是要重写 run 方法的原因

4、线程的终止

4.1 interrupt 方法停止
	public static int i = 0;
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                i++;
            }
            System.err.println(i);
        });
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        thread.interrupt();
    }

isInterrupted方法,默认是 false,interrupt方法,设置值为 true

找到源码,发现调用的是 Thread 里面的 interrupt ,接着往下找

// JVM.CPP
JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_Interrupt");
	... ...
  JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
  if (thr != NULL) {
    // 调用 Thread 里面的 interrupt
    Thread::interrupt(thr);
  }
JVM_END
// Thread.cpp
void Thread::interrupt(Thread* thread) {
  trace("interrupt", thread);
  debug_only(check_for_dangling_thread_pointer(thread);)
  // 通过底层操作系统,进行操作
  os::interrupt(thread);
}
// os_Linux.cpp
void os::interrupt(Thread* thread) {
  assert(Thread::current() == thread || Threads_lock->owned_by_self(),
    "possibility of dangling Thread pointer");
	// 获取本地线程对象
  OSThread* osthread = thread->osthread();

  if (!osthread->interrupted()) { // interrupted 默认值为 flase
    // 设置成 true
    osthread->set_interrupted(true);
      // 设置内存屏障,目的是使得 interrupted 状态对其他线程立即可见
    OrderAccess::fence();
    // SleepEvent 相当于 Thread.sleep ,若处于,则通过 unpark 进行解锁
    ParkEvent * const slp = thread->_SleepEvent ;
    if (slp != NULL) slp->unpark() ;
  }

  // For JSR166. Unpark even if interrupt status already was set
  if (thread->is_Java_thread())
    ((JavaThread*)thread)->parker()->unpark();
  // 相当于 sync 代码块中 object.wait 方法,调用 unpark 进行解锁
  ParkEvent * ev = thread->_ParkEvent ;
  if (ev != NULL) ev->unpark() ;
}
// opThread.hpp
volatile jint _interrupted; // 定义了一个 标记

// 调用此方法,进行标记的 赋值
void set_interrupted(bool z)                      { _interrupted = z ? 1 : 0; }

通过调用 interrupt 方法,会将 interrupted 属性进行赋值,并将进入 WAITING 状态的进行释放

通过这种方法,设置中断标记,优雅中断,并不是直接武断将线程停止,使线程在终止时有时间去清理资源等

4.2 interrupted 方法复位停止

​ interrupted 方法,对中断标记进行复位(恢复默认值)

Thread thread = new Thread(() -> {
            if (!Thread.currentThread().isInterrupted()) {
                System.err.println("线程当前状态: " + 				                					Thread.currentThread().isInterrupted());
                Thread.currentThread().interrupted();
                System.err.println("线程复位之后的状态: " + 				                				Thread.currentThread().isInterrupted());
            }
        });
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        thread.interrupt();
线程当前状态: false
线程复位之后的状态: false
// JVM.CPP
JVM_QUICK_ENTRY(jboolean, JVM_IsInterrupted(JNIEnv* env, jobject jthread, jboolean clear_interrupted))
  JVMWrapper("JVM_IsInterrupted");

  ... ...
  oop java_thread = JNIHandles::resolve_non_null(jthread);
  MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock);
  ... ...
  JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
  if (thr == NULL) {
    return JNI_FALSE;
  } else {
      // 调用了 Thread 中的 is_interrupted
    return (jboolean) Thread::is_interrupted(thr, clear_interrupted != 0);
  }
JVM_END
// Thread.cpp
bool Thread::is_interrupted(Thread* thread, bool clear_interrupted) {
  trace("is_interrupted", thread);
  debug_only(check_for_dangling_thread_pointer(thread);)
  // 调用底层操作
  return os::is_interrupted(thread, clear_interrupted);
}
// os_Linux.cpp
bool os::is_interrupted(Thread* thread, bool clear_interrupted) {
  assert(Thread::current() == thread || Threads_lock->owned_by_self(),
    "possibility of dangling Thread pointer");

  OSThread* osthread = thread->osthread();
	// 获得线程的中断状态
  bool interrupted = osthread->interrupted();

  // 对标记设置为 false ,恢复为默认值
  if (interrupted && clear_interrupted) {
    osthread->set_interrupted(false);
    // consider thread->_SleepEvent->reset() ... optional optimization
  }

  return interrupted;
}

进行复位,主要就是要告诉外界,我已经收到你的中断信号,但具体什么时候中断是由线程自己做决定。

4.3 阻塞方法抛出异常
		Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.err.println("收到信号");
                    break;
                }
            }
            System.err.println(i);
        });
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        thread.interrupt();

像 object.wait, object.sleep 的阻塞方法,都会抛出一个 InterruptedException 异常,通过对这个异常进行处理,达到终止线程的目的


转载:https://blog.csdn.net/qq_40293674/article/details/104699305
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场