Doug Lea 并发大师
学习方法:
场景-需求-解决方案-应用-原理
进程的生命周期:创建-销毁
Java中如何运用线程:
1,Runnable接口
2,Thread类(本质上是实现了Runnable接口)--使用较少,对资源不可控
3,ThreadPool线程池 --实际运动较多,风险(如 死锁,上下文切换等)
4,Callable/Future 带返回值的
线程可以合理利用多核心CPU资源,提高程序的吞吐量。--单核心行不行?
实际运用:责任链【略】
线程的生命周期:6种状态
public class Thread implements Runnable {
//...
public static enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
private State() {
}
}
//...
}
示意图
下面用代码演示一下线程的一些状态:waiting,timed_waiting,blocked
package com.gupaoedu.vip.pattern.mysingleton.hungry;
import java.util.concurrent.TimeUnit;
/**
* Created by huhuba on 2019/11/30.
*/
public class Test {
public static void main(String[] args) {
//线程状态转化演示
//1,RUNNING==>Time_Waiting
new Thread(()->{
while (true) {
try {
//相当于Thread.sleep(100*1000);
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"Time_Waiting_Thread").start();
//2,RUNNING==>Waiting
new Thread(()->{
while(true){
synchronized (Test.class){
try {
Test.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
},"Waiting_Thread").start();
//3,RUNNING==>BLOCKED
new Thread(new BlockedDemo(),"block_01_thread").start();
new Thread(new BlockedDemo(),"block_02_thread").start();
}
static class BlockedDemo extends Thread{
@Override
public void run() {
while(true){
synchronized (BlockedDemo.class){
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
执行上述代码后,打开终端,在该class文件所在目录执行 jps
E:\cms\code\huangxj\gupaoedu-vip-pattern-singleton\target\classes\com\gupaoedu\vip\pattern\mysingleton\hungry>jps
288 Test
6676 Launcher
4200 RemoteMavenServer
5784 Jps
8252
其中,288是目标进程号:
执行 jstack 288
得到当前3个线程各自的状态
"block_02_thread" #16 prio=5 os_prio=0 tid=0x0000000019362000 nid=0x19f0 waiting for monitor entry [0x0000000019f6f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.gupaoedu.vip.pattern.mysingleton.hungry.Test$BlockedDemo.run(Test.java:45)
- waiting to lock <0x00000000d5ed0658> (a java.lang.Class for com.gupaoedu.vip.pattern.mysingleton.hungry.Test$BlockedDemo)
at java.lang.Thread.run(Thread.java:748)
"block_01_thread" #14 prio=5 os_prio=0 tid=0x0000000019361800 nid=0x2e14 waiting on condition [0x0000000019e6e000]
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 com.gupaoedu.vip.pattern.mysingleton.hungry.Test$BlockedDemo.run(Test.java:45)
- locked <0x00000000d5ed0658> (a java.lang.Class for com.gupaoedu.vip.pattern.mysingleton.hungry.Test$BlockedDemo)
at java.lang.Thread.run(Thread.java:748)
"Waiting_Thread" #12 prio=5 os_prio=0 tid=0x000000001935e800 nid=0x2b90 in Object.wait() [0x0000000019d6f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000d5ba5d48> (a java.lang.Class for com.gupaoedu.vip.pattern.mysingleton.hungry.Test)
at java.lang.Object.wait(Object.java:502)
at com.gupaoedu.vip.pattern.mysingleton.hungry.Test.lambda$main$1(Test.java:26)
- locked <0x00000000d5ba5d48> (a java.lang.Class for com.gupaoedu.vip.pattern.mysingleton.hungry.Test)
at com.gupaoedu.vip.pattern.mysingleton.hungry.Test$$Lambda$2/1831932724.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Time_Waiting_Thread" #11 prio=5 os_prio=0 tid=0x000000001935a800 nid=0x1568 waiting on condition [0x0000000019c6f000]
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 com.gupaoedu.vip.pattern.mysingleton.hungry.Test.lambda$main$0(Test.java:15)
at com.gupaoedu.vip.pattern.mysingleton.hungry.Test$$Lambda$1/990368553.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
Thread.
习题:
1,线程的启动为什么是start?
实际上在OS层面创建一个java线程,并设置为RUNNABLE状态,在合适的时候来回调run方法
如果直接调用run方法,那就是调用了这个类的方法,并不会创建一个线程。
查看源码:start()--private native void start0();
--start0()源码查找:
1,下载hotspot源码/openjdk源码
中的42行找到一些native方法,其中就包含start0
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},
};
根据 JVM_StartThread,在hotspot文档中找到jvm.cpp文件
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
// We cannot hold the Threads_lock when we throw an exception,
// due to rank ordering issues. Example: we might need to grab the
// Heap_lock while we construct the exception.
bool throw_illegal_thread_state = false;
// We must release the Threads_lock before we can post a jvmti event
// in Thread::start.
{
// Ensure that the C++ Thread and OSThread structures aren't freed before
// we operate.
MutexLocker mu(Threads_lock);
// Since JDK 5 the java.lang.Thread threadStatus is used to prevent
// re-starting an already started thread, so we should usually find
// that the JavaThread is null. However for a JNI attached thread
// there is a small window between the Thread object being created
// (with its JavaThread set) and the update to its threadStatus, so we
// have to check for this
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));
// Allocate the C++ Thread structure and create the native thread. The
// stack size retrieved from java is signed, but the constructor takes
// size_t (an unsigned type), so avoid passing negative values which would
// result in really large stacks.
size_t sz = size > 0 ? (size_t) size : 0;
native_thread = new JavaThread(&thread_entry, sz);
// At this point it may be possible that no osthread was created for the
// JavaThread due to lack of memory. Check for this situation and throw
// an exception if necessary. Eventually we may want to change this so
// that we only grab the lock if the thread was created successfully -
// then we can also do this check and throw the exception in the
// JavaThread constructor.
if (native_thread->osthread() != NULL) {
// Note: the current thread is not being used within "prepare".
native_thread->prepare(jthread);
}
}
}
if (throw_illegal_thread_state) {
THROW(vmSymbols::java_lang_IllegalThreadStateException());
}
assert(native_thread != NULL, "Starting null thread?");
if (native_thread->osthread() == NULL) {
// No one should hold a reference to the 'native_thread'.
delete native_thread;
if (JvmtiExport::should_post_resource_exhausted()) {
JvmtiExport::post_resource_exhausted(
JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
"unable to create new native thread");
}
THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
"unable to create new native thread");
}
Thread::start(native_thread);
Thread::start(native_thread)这个方法在 Thread.cpp文件中
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);//操作系统的线程
}
}
2,线程的终止
Thread.interrupt()
import java.util.concurrent.TimeUnit;
/**
* Created by huhuba on 2019/11/30.
*/
public class Test {
static int i;
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
while(!Thread.currentThread().isInterrupted()){
i++;
}
System.out.println("i:"+i);
});
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt();//如果没有该步骤,线程将永远在while中死循环
}
}
不建议使用stop,因为这个相当于把线程直接杀死,比如linxu的kill -9,不安全
3,线程的复位
3.1,Thread.interrupted()
import java.util.concurrent.TimeUnit;
/**
* Created by huhuba on 2019/11/30.
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
while(true){
if(Thread.currentThread().isInterrupted()){
System.out.println("before:"+Thread.currentThread().isInterrupted());
Thread.interrupted();
System.out.println("after:"+Thread.currentThread().isInterrupted());
}
}
});
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt();
}
}
3.2,InterruptedException
import java.util.concurrent.TimeUnit;
/**
* Created by huhuba on 2019/11/30.
*/
public class Test {
static int i;
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
while(!Thread.currentThread().isInterrupted()){
try {
Thread.sleep(1000);//中断一个处于阻塞状态的异常,如join,wait,queue.take...都会抛异常
System.out.println("demo");//中断之后依然在执行
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
}
System.out.println("i:"+i);
});
thread.start();
TimeUnit.SECONDS.sleep(1);
thread.interrupt();//执行之后,会先去复位,然后抛出一个异常
System.out.println(thread.isInterrupted());//false
}
}
为什么需要复位?(没搞懂)
因为Thread.interrupted()属于当前线程对外部的一种回应,表示已经收到信号,但是不会立即中断。
--可以在异常中跳出循环,自然结束这个线程(还是不会立即停止当前线程)
正当手段结束阻塞:
wait--notify
sleep--倒计时
join--notify
并行和并发:
一条马路4车道,并行数是4
并发数是这条马路上能跑多少车,比如40辆车(再多就会拥堵)