- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
作者:小牛呼噜噜 | https://xiaoniuhululu.com 计算机内功、JAVA底层、面试、职业成长相关资料等更多精彩文章在公众号「 小牛呼噜噜 」 。
大家好,我是呼噜噜,最近一直在梳理Java并发,但内容杂且偏晦涩,今天我们一起来聊聊Java 线程的状态及转换 先来夯实一下基础,万丈高楼平地起,路还是得慢慢走.
我们先来看下Java线程的生命周期图:
上图也是本文的大纲,我们下面依次聊聊java各个线程状态及其他们的转换.
线程初始状态(NEW): 当前线程处于 线程被创建出来但没有被调用start() 。
在Java线程的时间中,关于线程的一切的起点是从Thread 类的对象的创建开始,一般实现Runnable接口 或者 继承Thread类的类,实例化一个对象出来,线程就进入了初始状态 。
Thread thread = new Thread()
由于线程在我们操作系统中也是非常宝贵的资源,在实际开发中,我们常常用 线程池 来重复利用现有的线程来执行任务,避免多次创建和销毁线程,从而降低创建和销毁线程过程中的代价。Java 给我们提供了 Executor 接口来使用线程池,查看其 JDK1.8源码 ,发现其内部封装了 Thread t = new Thread() 。
public class Executors {
...
static class DefaultThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
...
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
...
}
在thread类源码中,我们还能发现线程状态的枚举类 State :
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
所谓 线程的状态 ,在java源码中都是通过 threadStatus 的值来表示的 。
/* Java thread status for tools,
* initialized to indicate thread 'not yet started'
*/
private volatile int threadStatus = 0;
State 和 threadStatus 通过 toThreadState 方法映射转换 。
public State getState() {
// get current thread state
return sun.misc.VM.toThreadState(threadStatus);
}
//--- --- ---
public static State toThreadState(int var0) {
if ((var0 & 4) != 0) {
return State.RUNNABLE;
} else if ((var0 & 1024) != 0) {
return State.BLOCKED;
} else if ((var0 & 16) != 0) {
return State.WAITING;
} else if ((var0 & 32) != 0) {
return State.TIMED_WAITING;
} else if ((var0 & 2) != 0) {
return State.TERMINATED;
} else {
return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE;
}
}
到这里我们就可以发现, Thread t = new Thread() 在Java中只是设置了线程的状态,操作系统中并没有的实际线程的创建 。
线程运行状态(RUNNABLE),线程被调用了 start() 等待运行的状态 。
在Linux操作系统层面,包含 Running 和 Ready 状态。其中Ready状态是等待 CPU 时间片。现今主流的JVM,比如hotspot虚拟机都是把Java 线程,映射到操作系统OS底层的线程上,把调度委托给了操作系统。而操作系统比如Linux,它是多任务操作系统,充分利用CPU的高性能, 将CPU的时间分片 ,让单个CPU实现"同时执行"多任务的效果.
更多精彩文章在公众号「 小牛呼噜噜 」 。
Linux的任务调度又采用 抢占式轮转调度, 我们不考虑特权进程的话 , OS会选择在CPU上占用的时间最少进程,优先在cpu上分配资源,其对应的线程去执行任务,尽可能地维护任务调度公平。 Running 和 Ready 状态的线程在CPU中切换状态非常短暂。大概只有 0.01 秒这一量级,区分开来意义不大,java将这2个状态统一用 RUNNABLE 来表示 。
我们接下来看看为什么说执行 thread.start() 后,线程的才"真正的创建" 。
public class ThreadTest {
/**
* 继承Thread类
*/
public static class MyThread extends Thread {
@Override
public void run() {
System.out.println("This is child thread");
}
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
其中 thread.start() 方法的源码中,会去调用 start0() 方法,而 start0() 是 private native void start0(); JVM调用Native方法的话,会进入到不受JVM控制的世界里 。
在 Thread类 实例化的同时,会首先调用 registerNatives 方法,注册本地Native方法,动态绑定JVM方法 。
private static native void registerNatives();
static {
registerNatives();
}
在 Thread 类中通过 registerNatives 将指定的本地方法绑定到指定函数,比如 start0 本地方法绑定到 JVM_StartThread 函数:
...
static JNINativeMethod methods[] = {
{"start0", "()V", (void *)&JVM_StartThread},
{"stop0", "(" OBJ ")V", (void *)&JVM_StopThread},
{"isAlive", "()Z", (void *)&JVM_IsThreadAlive},
...
源码见: http://hg.openjdk.java.net/jdk8u/jdk8u60/jdk/file/935758609767/src/share/native/java/lang/Thread.c 。
JVM_StartThread 是JVM层函数,抛去各种情况的处理,主要是通过 new JavaThread(&thread_entry, sz) 来创建 JVM线程对象 。
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
//表示是否有异常,当抛出异常时需要获取Heap_lock。
bool throw_illegal_thread_state = false;
// 在发布jvmti事件之前,必须释放Threads_lock
// in Thread::start.
{
// 获取 Threads_lock锁
MutexLocker mu(Threads_lock);
if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
throw_illegal_thread_state = true;
} else {
// We could also check the stillborn flag to see if this thread was already stopped, but
// for historical reasons we let the thread detect that itself when it starts running
jlong size =
java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
// 创建JVM线程(用JavaThread对象表示)
size_t sz = size > 0 ? (size_t) size : 0;
native_thread = new JavaThread(&thread_entry, sz);
...
}
}
...
Thread::start(native_thread);//启动内核线程
JVM_END
源码见: https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/prims/jvm.cpp 。
我们再来看看 JavaThread 的实现,发现内部通过 os::create_thread(this, thr_type, stack_sz); 来调用不同操作系统的创建线程方法创建线程.
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);
// Create the native thread itself.
// %note runtime_23
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);//调用不同操作系统的创建线程方法创建线程
}
源码见: https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/runtime/thread.cpp 。
我们都知道Java是跨平台的,但是native各种方法底层c/c++代码对各平台都需要有对应的兼容,我们这边以linux为例,其他平台就大家自行去查阅了 。
bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
assert(thread->osthread() == NULL, "caller responsible");
// Allocate the OSThread object
OSThread* osthread = new OSThread(NULL, NULL);
if (osthread == NULL) {
return false;
}
// set the correct thread state
osthread->set_thread_type(thr_type);
// Initial state is ALLOCATED but not INITIALIZED
osthread->set_state(ALLOCATED);
thread->set_osthread(osthread);
// init thread attributes
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// stack size
if (os::Linux::supports_variable_stack_size()) {
// calculate stack size if it's not specified by caller
if (stack_size == 0) {
stack_size = os::Linux::default_stack_size(thr_type);
switch (thr_type) {
case os::java_thread:
// Java threads use ThreadStackSize which default value can be
// changed with the flag -Xss
assert (JavaThread::stack_size_at_create() > 0, "this should be set");
stack_size = JavaThread::stack_size_at_create();
break;
case os::compiler_thread:
if (CompilerThreadStackSize > 0) {
stack_size = (size_t)(CompilerThreadStackSize * K);
break;
} // else fall through:
// use VMThreadStackSize if CompilerThreadStackSize is not defined
case os::vm_thread:
case os::pgc_thread:
case os::cgc_thread:
case os::watcher_thread:
if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K);
break;
}
}
stack_size = MAX2(stack_size, os::Linux::min_stack_allowed);
pthread_attr_setstacksize(&attr, stack_size);
} else {
// let pthread_create() pick the default value.
}
// glibc guard page
pthread_attr_setguardsize(&attr, os::Linux::default_guard_size(thr_type));
ThreadState state;
{
// Serialize thread creation if we are running with fixed stack LinuxThreads
bool lock = os::Linux::is_LinuxThreads() && !os::Linux::is_floating_stack();
if (lock) {
os::Linux::createThread_lock()->lock_without_safepoint_check();
}
pthread_t tid;
//通过pthread_create方法创建内核级线程 !
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
pthread_attr_destroy(&attr);
if (ret != 0) {
if (PrintMiscellaneous && (Verbose || WizardMode)) {
perror("pthread_create()");
}
// Need to clean up stuff we've allocated so far
thread->set_osthread(NULL);
delete osthread;
if (lock) os::Linux::createThread_lock()->unlock();
return false;
}
// Store pthread info into the OSThread
osthread->set_pthread_id(tid);
// Wait until child thread is either initialized or aborted
{
Monitor* sync_with_child = osthread->startThread_lock();
MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag);
while ((state = osthread->get_state()) == ALLOCATED) {
sync_with_child->wait(Mutex::_no_safepoint_check_flag);
}
}
if (lock) {
os::Linux::createThread_lock()->unlock();
}
}
// Aborted due to thread limit being reached
if (state == ZOMBIE) {
thread->set_osthread(NULL);
delete osthread;
return false;
}
// The thread is returned suspended (in state INITIALIZED),
// and is started higher up in the call chain
assert(state == INITIALIZED, "race condition");
return true;
}
源码见: https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/os/linux/vm/os_linux.cpp 。
主要通过 pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread) ,它是unix 创建线程的方法,linux也继承了。调用后在linux系统中会创建一个内核级的线程。 也就是说这个时候操作系统中线程才真正地诞生 。
更多精彩文章在公众号「 小牛呼噜噜 」 。
但此时线程才诞生,那是怎么启动的?我们回到 JVM_StartThread 源码中, Thread::start(native_thread) 很明显这行代码就表示启动 native_thread = new JavaThread(&thread_entry, sz) 创建的线程,我们来继续看看其源码 。
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()) {
// 设置线程状态
java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
java_lang_Thread::RUNNABLE);
}
os::start_thread(thread);
}
}
源码: https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/runtime/thread.cpp 。
os::start_thread 它封装了 pd_start_thread(thread) ,执行该方法,操作系统会去启动指定的线程 。
void os::start_thread(Thread* thread) {
// guard suspend/resume
MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);
OSThread* osthread = thread->osthread();
osthread->set_state(RUNNABLE);
pd_start_thread(thread);
}
当操作系统的线程启动完之后,我们再回到 pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread) ,会去 java_start 这个线程入口函数进行OS内核级线程的初始化,并开始启动 JavaThread 。
// Thread start routine for all newly created threads
static void *java_start(Thread *thread) {
// Try to randomize the cache line index of hot stack frames.
// This helps when threads of the same stack traces evict each other's
// cache lines. The threads can be either from the same JVM instance, or
// from different JVM instances. The benefit is especially true for
// processors with hyperthreading technology.
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();
// non floating stack LinuxThreads needs extra check, see above
if (!_thread_safety_check(thread)) {
// notify parent thread
MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
osthread->set_state(ZOMBIE);
sync->notify_all();
return NULL;
}
// thread_id is kernel thread id (similar to Solaris LWP id)
osthread->set_thread_id(os::Linux::gettid());
if (UseNUMA) {
int lgrp_id = os::numa_get_group_id();
if (lgrp_id != -1) {
thread->set_lgrp_id(lgrp_id);
}
}
// initialize signal mask for this thread
os::Linux::hotspot_sigmask(thread);
// initialize floating point control register
os::Linux::init_thread_fpu_state();
// handshaking with parent thread
{
MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
// notify parent thread
osthread->set_state(INITIALIZED);
sync->notify_all();
// 等待,直到操作系统级线程全部启动
while (osthread->get_state() == INITIALIZED) {
sync->wait(Mutex::_no_safepoint_check_flag);
}
}
// 开始运行JavaThread::run
thread->run();
return 0;
}
源码: https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/os/linux/vm/os_linux.cpp 。
thread->run() 其实就是 JavaThread::run() 也表明方法开始回调,从OS层方法回到JVM层方法 ,我们再来看下其实现:
// The first routine called by a new Java thread
void JavaThread::run() {
// initialize thread-local alloc buffer related fields
this->initialize_tlab();
// used to test validitity of stack trace backs
this->record_base_of_stack_pointer();
// Record real stack base and size.
this->record_stack_base_and_size();
// Initialize thread local storage; set before calling MutexLocker
this->initialize_thread_local_storage();
this->create_stack_guard_pages();
this->cache_global_variables();
// Thread is now sufficient initialized to be handled by the safepoint code as being
// in the VM. Change thread state from _thread_new to _thread_in_vm
ThreadStateTransition::transition_and_fence(this, _thread_new, _thread_in_vm);
assert(JavaThread::current() == this, "sanity check");
assert(!Thread::current()->owns_locks(), "sanity check");
DTRACE_THREAD_PROBE(start, this);
// This operation might block. We call that after all safepoint checks for a new thread has
// been completed.
this->set_active_handles(JNIHandleBlock::allocate_block());
if (JvmtiExport::should_post_thread_life()) {
JvmtiExport::post_thread_start(this);
}
JFR_ONLY(Jfr::on_thread_start(this);)
// We call another function to do the rest so we are sure that the stack addresses used
// from there will be lower than the stack base just computed
thread_main_inner();//!!!注意此处方法
// Note, thread is no longer valid at this point!
}
void JavaThread::thread_main_inner() {
assert(JavaThread::current() == this, "sanity check");
assert(this->threadObj() != NULL, "just checking");
// Execute thread entry point unless this thread has a pending exception
// or has been stopped before starting.
// Note: Due to JVM_StopThread we can have pending exceptions already!
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);//JavaThread对象中传入的entry_point为Thread对象的Thread::run方法
}
DTRACE_THREAD_PROBE(stop, this);
this->exit(false);
delete this;
}
源码: https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/runtime/thread.cpp 。
由于 JavaThread 定义可知 JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) 中参数 entry_point 是外部传入,那我们想想 JavaThread 是什么时候实例化的?
没错,就是我们一开始的 JVM_StartThread 中 native_thread = new JavaThread(&thread_entry, sz); 也就是说 this->entry_point()(this, this) 实际上是回调的 thread_entry 方法 。
thread_entry 源码:
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);
}
源码: https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/prims/jvm.cpp 通过 JavaCalls::call_virtual 方法,又从JVM层 回到了Java语言层 ,即 MyThread thread = new MyThread(); thread.start(),
一切又回到了起点,这就是Java thread.start() 内部完整的一个流程, HotSpot虚拟机 实现的Java线程其实是对Linux内核级线程的直接映射,将Java涉及到的 所有线程调度、内存分配都交由操作系统进行管理 .
线程终止状态(TERMINATED),表示该线程已经运行完毕.
当一个线程执行完毕,或者主线程的main()方法完成时,我们就认为它终止了。终止的线程无法在被使用,如果调用 start() 方法,会抛出 java.lang.IllegalThreadStateException 异常,这一点我们可以从start源码中很容易地得到 。
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
...
}
线程阻塞状态(BLOCKED),需要 等待锁释放 或者说 获取锁失败 时,线程阻塞 。
public class BlockedThread implements Runnable {
@Override
public void run() {
synchronized (BlockedThread.class){
while (true){
}
}
}
}
从Thread源码的注释中,我们可以知道 等待锁释放 或者说 获取锁失败 ,主要有下面3中情况:
其中第三种情况,大家可以先思考一下,我们留在下文 线程等待状态 再详细展开 。
线程等待状态(WAITING),表示该线程需要等待其他线程做出一些特定动作(通知或中断).
我们紧接着上一小节,调用 wait 后, 重新进入synchronized 方法/块时 ,我们来看看期间发生了什么?
当 线程1 调用对象A的 wait 方法后,会释放当前的锁,然后让出CPU时间片,线程会进入该对象的 等待队列中 ,线程状态变为 等待状态WAITING 。 当另一个 线程2 调用了对象A的 notify()/notifyAll() 方法 。
notify()方法只会唤醒沉睡的线程,不会立即释放之前占有的对象A的锁,必须执行完notify()方法所在的synchronized代码块后才释放。所以在编程中,尽量在使用了notify/notifyAll()后立即退出临界区 。
线程1 收到通知后退出等待队列,并进入 线程运行状态RUNNABLE ,等待 CPU 时间片分配, 进而执行后续操作,接着 线程1 重新进入 synchronized 方法/块时,竞争不到锁,线程状态变为 线程阻塞状态BLOCKED 。如果竞争到锁,就直接接着运行。线程等待状态 切换到线程阻塞状态,无法直接切换,需要经过线程运行状态.
我们再来看一个例子,巩固巩固:
public class WaitNotifyTest {
public static void main(String[] args) {
Object A = new Object();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程1等待获取 对象A的锁...");
synchronized (A) {
try {
System.out.println("线程1获取了 对象A的锁");
Thread.sleep(3000);
System.out.println("线程1开始运行wait()方法进行等待,进入到等待队列......");
A.wait();
System.out.println("线程1等待结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程2等待获取 对象A的锁...");
synchronized (A) {
System.out.println("线程2获取了 对象A的锁");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程2将要运行notify()方法进行唤醒线程1");
A.notify();
}
}
}).start();
}
}
结果:
线程1等待获取 对象A的锁...
线程1获取了 对象A的锁
线程2等待获取 对象A的锁...
线程1开始运行wait()方法进行等待,进入到等待队列......
线程2获取了 对象A的锁
线程2将要运行notify()方法进行唤醒线程1
线程1等待结束
需要注意的是, wait/notify/notifyAll 只能在synchronized修饰的方法、块中使用 , notify 是只随机唤醒一个线程,而 notifyAll 是唤醒所有等待队列中的线程 。
Thread类中的join方法的主要作用 能让线程之间的并行执行变为串行执行 ,当前线程等该加入该线程后面,等待该线程终止 。
public static void main(String[] args) {
Thread thread = new Thread();
thread.start();
thread.join();
...
}
上面一个例子表示,程序在main主线程中调用thread线程的join方法,意味着main线程放弃CPU时间片(主线程会变成 WAITING 状态),并返回thread线程,继续执行直到线程thread执行完毕,换句话说 在主线程执行过程中,插入thread线程,还得等thread线程执行完后,才轮到主线程继续执行 。
如果查看JDK thread.join() 底层实现,会发现其实内部封装了 wait(),notifyAll() 。
LockSupport.park() 挂起当前线程;LockSupport.unpark(暂停线程对象) 恢复某个线程 。
package com.zj.ideaprojects.demo.test3;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.LockSupport;
public class ThreadLockSupportTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
System.out.println("start.....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("park....");
LockSupport.park();
System.out.println("resume.....");
});
thread.start();
Thread.sleep(3000);
System.out.println("unpark....");
LockSupport.unpark(thread);
}
}
结果:
start.....
park....
unpark....
resume.....
当程序调用 LockSupport.park() ,会让当前线程A的线程状态会从 RUNNABLE 变成 WAITING,然后main主线程调用 LockSupport.unpark(thread) ,让指定的线程即线程A,从 WAITING 回到 RUNNABLE 。我们可以发现 park/unpark 和 wait/notify/notifyAll 很像,但是他们有以下的区别:
超时等待状态(TIMED_WAITING),也叫 限期等待 ,可以在指定的时间后自行返回而不是像 WAITING 那样一直等待.
这部分比较简单,它和线程等待状态(WAITING)状态 非常相似,区别就是方法的参数舒服传入限制时间,在 Timed Waiting 状态时会等待超时,之后由系统唤醒,或者也可以提前被通知唤醒如 notify 。
相关方法主要有:
1. Object.wait(long)
2. Thread.join(long)
3. LockSupport.parkNanos(long)
4. LockSupport.parkUntil(long)
5. Thread.sleep(long)
需要注意的是 Thread.sleep(long) ,当线程执行 sleep 方法时,不会释放当前的锁(如果当前线程进入了同步锁),也不会让出CPU。 sleep(long) 可以用指定时间使它自动唤醒过来,如果时间不到只能调用 interrupt 方法强行打断.
参考资料:
https://hg.openjdk.java.net/jdk8u 。
《并发编程的艺术》 。
https://www.jianshu.com/p/216a41352fd8 。
本篇文章到这里就结束啦,如果我的文章对你有所帮助,还请帮忙一键三连: 点赞、关注、收藏 ,你的支持会激励我输出更高质量的文章,感谢! 。
原文镜像: 原来还能这样看Java线程的状态及转换 。
计算机内功、源码解析、科技故事、项目实战、面试八股等更多硬核文章,首发于公众号「 小牛呼噜噜 」,我们下期再见.
最后此篇关于原来还能这样看Java线程的状态及转换的文章就讲到这里了,如果你想了解更多关于原来还能这样看Java线程的状态及转换的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在尝试将一个字符串逐个字符地复制到另一个字符串中。目的不是复制整个字符串,而是复制其中的一部分(我稍后会为此做一些条件......) 但我不知道如何使用迭代器。 你能帮帮我吗? std::stri
我想将 void 指针转换为结构引用。 结构的最小示例: #include "Interface.h" class Foo { public: Foo() : mAddress((uint
这有点烦人:我有一个 div,它从窗口的左上角开始过渡,即使它位于文档的其他任何位置。我试过 usign -webkit-transform-origin 但没有成功,也许我用错了。有人可以帮助我吗?
假设,如果将 CSS3 转换/转换/动画分配给 DOM 元素,我是否可以检测到该过程的状态? 我想这样做的原因是因为我正在寻找类似过渡链的东西,例如,在前一个过渡之后运行一个过渡。 最佳答案 我在 h
最近我遇到了“不稳定”屏幕,这很可能是由 CSS 转换引起的。事实上,它只发生在 Chrome 浏览器 上(可能还有 Safari,因为一些人也报告了它)。知道如何让它看起来光滑吗?此外,您可能会注意
我正在开发一个简单的 slider ,它使用 CSS 过渡来为幻灯片设置动画。我用一些基本样式和一些 javascript 创建了一支笔 here .注意:由于 Codepen 使用 Prefixfr
我正在使用以下代码返回 IList: public IList FindCodesByCountry(string country) { var query =
如何设计像这样的操作: 计算 转化 翻译 例如:从“EUR”转换为“CNY”金额“100”。 这是 /convert?from=EUR&to=CNY&amount=100 RESTful 吗? 最佳答
我使用 jquery 组合了一个图像滚动器,如下所示 function rotateImages(whichHolder, start) { var images = $('#' +which
如何使用 CSS (-moz-transform) 更改一个如下所示的 div: 最佳答案 你可以看看Mozilla Developer Center .甚至还有例子。 但是,在我看来,您的具体示例不
我需要帮助我正在尝试在选中和未选中的汉堡菜单上实现动画。我能够为菜单设置动画,但我不知道如何在转换为 0 时为左菜单动画设置动画 &__menu { transform: translateX(
我正在为字典格式之间的转换而苦苦挣扎:我正在尝试将下面的项目数组转换为下面的结果数组。本质上是通过在项目第一个元素中查找重复项,然后仅在第一个参数不同时才将文件添加到结果集中。 var items:[
如果我有两个定义相同的结构,那么在它们之间进行转换的最佳方式是什么? struct A { int i; float f; }; struct B { int i; float f; }; void
我编写了一个 javascript 代码,可以将视口(viewport)从一个链接滑动到另一个链接。基本上一切正常,你怎么能在那里看到http://jsfiddle.net/DruwJ/8/ 我现在的
我需要将文件上传到 meteor ,对其进行一些图像处理(必要时进行图像转换,从图像生成缩略图),然后将其存储在外部图像存储服务器(s3)中。这应该尽可能快。 您对 nodejs 图像处理库有什么建议
刚开始接触KDB+,有一些问题很难从Q for Mortals中得到。 说,这里 http://code.kx.com/wiki/JB:QforMortals2/casting_and_enumera
我在这里的一个项目中使用 JSF 1.2 和 IceFaces 1.8。 我有一个页面,它基本上是一大堆浮点数字段的大编辑网格。这是通过 inputText 实现的页面上的字段指向具有原始值的值对象
ScnMatrix4 是一个 4x4 矩阵。我的问题是什么矩阵行对应于位置(ScnVector3),旋转(ScnVector4),比例(ScnVector3)。第 4 行是空的吗? 编辑: 我玩弄了
恐怕我是 Scala 新手: 我正在尝试根据一些简单的逻辑将 Map 转换为新 Map: val postVals = Map("test" -> "testing1", "test2" -> "te
输入: This is sample 1 This is sample 2 输出: ~COLOR~[Green]This is sample 1~COLOR~[Red]This is sam
我是一名优秀的程序员,十分优秀!