当前位置:网站首页>JUC进阶-NO.1 线程基础知识复习
JUC进阶-NO.1 线程基础知识复习
2022-07-20 07:42:00 【社畜阿藏405】
NO.1 线程基础知识复习
文章目录
NO.1 线程基础知识复习
一. 前言
1. 四大口诀
- 高内聚低耦合前提下,封装思想
- 判断,干活,通知
- 防止虚假唤醒,wait方法要注意
- 注意标志位flag,可能是volatile的
2.为什么多线程极其重要?
硬件方面
摩尔定律失效
摩尔定律:
他是由英特尔创始人之一戈登·摩尔提出的,其内容为:
当价格不变时,集成电路可容纳的元器件的数目约每隔18-24个月便会增加一倍,性能也将提升一倍.
这一定律揭示了信息技术进步的速度
可是从2003年开始CPU主频已经不再翻倍,而是采用多核心而不是更快的主频
-> 摩尔定律失效
在主频不再提升且核心数在不断增加的情况下,要想让程序更快就要用到并行或并发编程.
软件方面
高并发系统,异步回调等生产需求
二. 线程的start方法
1.Java线程理解以及openjdk中的实现
private native void start0();
native关键字修饰的方法,标识通过jvm调用底层操作系统的函数方法.
JNI: java native interface
Java语言本身底层就是C++
Thread.c -> \jdk\src\share\native\java\lang
java线程是通过start的方法启动执行的,主要内容在native方法start0中,
openjdk的写JNI一般是意义对应的,Thread.java对应的就是Thread.c
start0其实就是JVM_StartThread.此时查看源代码可以看到在jvm.h中找到了声明,jvm.cpp中有实现
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}, };
jvm.cpp -> \hotspot\src\share\vm\prims
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); JVM_END
thread.cpp -> \hotspot\src\share\vm\runtime
os: 操作系统
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); } }
三. Java多线程相关概念
1.进程
进程是程序的一次执行,是系统进行资源分配和调度的独立单位,每一个进程都有他自己的内存空间和系统资源
2.线程
在同一个进程内又可以有多个任务,而着每一个任务我们就可以看做是一个线程
一个进程会有一个或多个线程的
3.面试题: 何为进程和线程?
计算机的核心是CPU,他承担了所有的计算任务.她就像一座工厂,时刻在运行
假定工厂的电力有限,一次只能供给一个车间使用.也就是说,一个车间开工的时候,其他车间都必须停工.背后的含义就是,单个CPU一次只能运行一个任务.
进程就好比工厂的车间,他代表CPU所能处理的单个任务,任一时刻,CPU总是运行一个进程,其他进程处于非运行状态.
一个车间可以有多个工人(线程),他们协同完成一个任务
车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的.这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存
可是每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所.里面有人的时候,其他人就不能进去了.这代表一个线程使用某些共享内存时,其他线程必须等他结束,才能使用这一块内存
一个防止他人进入的简单方法,就是门口加一把锁.先到的人锁上门,后到 的人看到上锁,就在门口排队,等锁打开再进去.这就叫"互斥锁"(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域
还有些房间,可以同事容纳n个人,.也就是说,如果人数大于n,多出来的人只能在外面等着.这好比某些内存区域,只能供给固定数目的线程 ->(semphore或者线程池)
这时的解决方法,就是门口挂n把钥匙.进去的人就取一把要是,出来时再把要是挂回原处.后到的人发现要是没有了,就知道必须在门口排队等着了.这种做法叫做"信号量"(Semaphore),用来保证多个线程不会互相冲突.
不难看出,mutex是semaphore的一种情况(n=1时).也就是说,完全可以用后者替代前者.但是,因为mutex较为简单,且效率高,所以在必须保持资源独占的情况下,还是采用这种设计
操作系统的设计,因此可以归结为三点:
- 以多进程形式,允许多个任务同时进行;
- 以多线程形势,允许单个任务分成不同的不分运行
- 提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源
一般人不明白semaphore(调度线程)和mutex(互斥锁)的区别,根本原因是不知道semaphore的用途。
semaphore的用途,一句话:调度线程。
有的人用semaphore也可以把上面例子中的票“保护"起来以防止共享资源冲突,必须承认这是可行的,但是semaphore不是让你用来做这个的;如果你要做这件事,请用mutex。
在网上、包括stackoverflow等著名论坛上,有一个流传很广的厕所例子:
mutex是一个厕所一把钥匙,谁抢上钥匙谁用厕所,谁没抢上谁就等着;semaphore是多个同样厕所多把同样的钥匙 ---- 只要你能拿到一把钥匙,你就可以随便找一个空着的厕所进去。
事实上,这个例子对初学者、特别是刚刚学过mutex的初学者来说非常糟糕 ----- 我第一次读到这个例子的第一反应是:semaphore是线程池???所以,请务必忘记这个例子。
另外,有人也会说:mutex就是semaphore的value等于1的情况。
这句话不能说不对,但是对于初学者来说,请先把这句话视为错误;等你将来彻底融会贯通这部分知识了,你才能真正理解上面这句话到底是什么意思。总之请务必记住:mutex干的活儿和semaphore干的活儿不要混起来。
4.管程
Monitor(监视器),也就是我们平时说的锁
Monitor其实是一种同步机制,他的义务是保证(同一时间)只有一个线程可以访问被保护的数据和代码
JVM中同步是基于进入和退出监视器对象(Monitor管程对象)来实现的,每个对象实例都会有一个Monitor对象
public static void main(String[] args) { Object o = new Object(); new Thread(() -> { synchronized (o){ } }, "t1").start(); }
Monitor对象会和Java对象一同创建并销毁,她底层是由C++语言来实现的
JVM第三版
JVM可以支持方法级的同步和方法内部一段指令序列的同步,这两种同步结构都是使用管程(Monitor,更常见的是直接将他称为"锁")来实现的.
方法级的同步是隐式的,无需通过字节码指令来控制,她实现在方法调用和操作之中.JVM可以从方法常量池中的方法表结构中的ACC_SYNCHRONIZED访问标志得知一个方法是否被声明为同步方法.当方法调用时,调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置,入锅设置了,执行线程就要求先成功持有管程,然后才能执行方法,最后当方法完成(无论是正常完成还是非正常完成)时释放管程.在方法执行期间,执行线程持有了管程,其他任何线程都无法再获取到同一个管程.如果一个同步方法执行期间抛出异常,并且再方法内部无法处理此异常,那这个同步方法所持有的管程将在异常抛到同步方法边界之外时自动释放.
同步一段指令集序列通常是由Java语言中的synchronized语句块来表示的,JVM的指令集有monitorenter和monitorexit两条指令来支持synchronized关键字的语义,正确实现synchronized关键子需要Javac编译器与JVM两者共同协作支持
四. 用户线程和守护线程
Java线程分为用户线程和守护线程,线程的daemon属性为true标识守护线程,否则为用户线程
1.守护线程
是一种特殊的线程,在后台默默地完成一些系统行的服务,比如垃圾回收线程
- 当程序中所有用户线程执行完毕之后,不管守护线程是否结束,系统都会自动结束
- 设置守护线程,需要再start()方法之前进行
- 如果用户线程全部结束了,意味着程序需要完成的业务操作已经结束了,系统可以推出了.所以当系统只剩下守护进程的时候,JVM会自动退出
2.用户线程
是系统的工作线程,他会完成这个程序需要完成的业务操作
边栏推荐
- Openresty缓存使用
- [learning notes] agc016
- Leetcode force deduction solution - 30 Concatenate substrings of all words
- 10 talk about ThreadLocal
- go 多路复用
- localtime()
- 【Verilog数字系统设计(夏宇闻)----Verilog的基础知识1】
- Qt 多线程实现的两种方式 线程实现
- [recursion & divide and conquer] compressed transformation (using interval tree and dichotomy, recursively count the number types in the specified interval)
- Swift reads the bundle file
猜你喜欢
随机推荐
10 talk about ThreadLocal
Jmeter关联
Jenkins插件开发——插件的拓展
pypi 统计下载次数
What is ethanol?
go sync包
解决你的绩效管理问题的3种方法
Don't use the line segment tree to solve all my schedule problems with one idea
Iptables防火墙实验
Using hashes to solve problems
postgreSQL里怎么切换结束符?
[learning notes] agc016
How much money do young people who do 3D, digital people and metauniverse make?
[in simple terms, play with fpga9 ----- experience drops]
How JMeter threads execute sequentially -- test plan configuration
Daily question 1: specified interval reversal in the linked list
The use and difference of extern and static as global variables
小程序毕设作品之微信运动场地预约小程序毕业设计(6)开题答辩PPT
Redis lock oversold to solve the problem
每日三题 7.15