In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-17 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly introduces Java thread state + thread safety +synchronized usage is what the relevant knowledge, detailed and easy to understand, simple and fast operation, with a certain reference value, I believe that everyone reading this Java thread state + thread safety +synchronized usage is what the article will gain, let's take a look at it.
Thread state in java
At the operating system level, a thread has two states: ready and blocking.
But java in order to be able to more quickly know the cause of a thread blocking when blocking, and the blocking state of the refinement.
NEW: The thread object has been created, but the system-level thread has not yet been created, or the thread object has not yet called start()
TERMINATED: The thread in the system has been destroyed, but the thread object in the code is still there, that is, run() has run out, and the Thread object is still there.
RUNNABLE: Threads are in the ready queue and may be scheduled for execution by the CPU at any time.
TIMED_WAITING: During thread execution, the thread object calls sleep(), enters a block, and when the sleep time is up, it will return to the ready queue.
BLOCKED: After one thread synchronizes an object, another thread tries to lock the object, and it falls into the BLOCKED state. Only when the first thread unlocks the lock object can the next thread lock the object.
WAITING: with synchronized to use wait(), once a thread calls wait(), will first unlock the object, wait until another thread notify(), after waiting in the thread will be awakened, of course, you can also set a maximum wait time in wait(), to prevent death and so on.
Thread Safety Case Study Multiple threads write to the same variable
Concept: When is a string of code thread-safe? First of all, the root of thread safety problem is that when multi-thread concurrent execution, there will be preemptive execution phenomenon, here preemptive execution, the execution of machine instructions! When is a string of code thread-safe? Multi-thread concurrency, regardless of how several threads preemptively execute their code, will not affect the final result, called thread safety, but because of preemptive execution, there is a different result than expected, called a thread safety problem, a bug!
Typical case: use two threads to perform self-increment operation on the same number 10w times:
public class Demo1 { private static int count=0; public static void main(String[] args) { Thread t1=new Thread(()->{ for(int i=0;i{ t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(count); }}//Print result:68994
Obviously, the expected result is 10w, but the calculation is more than 6w, which is a thread safety problem.
Reasons for analysis:
Only for each thread heap count increment operation: First of all, understand that a machine instruction to do a self increment has three steps: from main memory to the count value into the cpu register-> register the count value of the self increment 1-> register the count value into the main memory, let us call these three steps:load->add->save
We assume that two sets of instructions are executed concurrently on a CPU (drawing two CPUs is a good representation)(there will be no such situation as simultaneous load):
As shown above:
Observation found that: two threads are executed once count++, but two ++ results are unsatisfactory, equivalent to only one self-increment, the above is a thread safety problem.
And we can predict the result range of the above code: between 5w-10w!, Why?
The above two diagrams represent the occurrence of thread safety problems, the performance of the results is twice plus when a go to use, if the two threads have been in such a state (is also the worst state), not that the calculation result is 5w Luo, then if the two threads have been a thread complete execution of load-add-save, another thread to perform such an operation, then the serial execution, not 10w Luo.
3. How to solve the above cases?
The last case also mentioned, as long as can achieve serial execution, can guarantee the correctness of the result, that java does have such a function for us to use, namely synchronized keyword use.
That is to say:CPU1 locks the lock object before executing load, and then unlocks it after saving. CPU2 can lock the object at this time and perform a series of operations. In this case, the atomicity of load-add-save is guaranteed, so that these three steps are either not executed or executed in one breath.
So you might ask, what's the difference between this and just using one main thread to compute self increment 10w times, what's the point of creating multiple threads?
Meaning is great, because we create a thread many times more than just an operation, light for self-increasing we can prevent thread safety problems by locking, but other operations of the thread if not thread safety problems that can be concurrent ah, then not greatly enhance the execution efficiency of the cough.
4. How exactly do you lock it?
Here only say a lock way, first to solve the above-mentioned case problems to say.
Use the keyword synchronized, which is used to synchronize ordinary methods (synchronized can also modify code blocks and static methods)
class Counter{ private int count; synchronized public void increase(){ this.count++; } public int getCount(){ return this.count; }}public class Demo2 { private static int num=50000; public static void main(String[] args) { Counter counter=new Counter();//the count value in the object defaults to 0 Thread t1=new Thread(()->{ for (int i = 0; i
< num; i++) { counter.increase(); } }); t1.start(); Thread t2=new Thread(()->{ for (int i = 0; i
< num; i++) { counter.increase(); } }); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(counter.getCount()); }}//打印10W内存可见性问题 首先说明:这是有编译器优化导致的,其次要知道cpu读取变量时:先从主内存将变量的值存至缓存或者寄存器中,cpu计算时再在寄存器中读取这个值. 当某线程频繁的从内存中读取一个不变的变量时,编译器将会把从内存获取变量的值直接优化成从寄存器直接获取.之所以这样优化,是因为,cpu从主内存中读取一个变量比在缓存或者寄存器中读取一个变量的值慢成千上万倍,如果每每在内存中读到的都是同一个值,既然缓存里头已经有这个值了,干嘛还大费周折再去主内存中进行获取呢,直接从缓存中直接读取就可以了,可提升效率. 但是:一旦一个线程被优化成上述的情况,那如果有另一个线程把内存中的值修改了,我被优化的线程还傻乎乎的手里拿着修改之前的值呢,或者内存中的变量值被修改了,被优化的线程此时已经感应不到了. 具体而言: public class Demo3 { private static boolean flag=false; public static void main(String[] args) { Thread t1=new Thread(()->{ while(!flag){ System.out.println("我是优化完之后直接读取寄存器中的变量值才打印的哦!"); } }); t1.start(); flag=true; System.out.println("我已经在主线程中修改了标志位"); }}
运行上述代码之后,程序并不会终止,而是一直在那打印t1线程中的打印语句.
如何解决上述问题:
引入关键字volatile:防止内存可见性问题,修饰一个变量,那某线程想获取该变量的值的时候,只能去主内存中获取,其次它还可以防止指令重排序,指令重排问题会在线程安全的单例模式(懒汉)进行介绍.具体:
public class Demo3 { private static volatile boolean flag=false; public static void main(String[] args) { Thread t1=new Thread(()->{ while(!flag){ System.out.println("我是优化完之后直接读取寄存器中的变量值才打印的哦!"); } }); t1.start(); try { Thread.sleep(1);//主线程给t1留有充足的时间先跑起来 } catch (InterruptedException e) { e.printStackTrace(); } flag=true; System.out.println("我已经在主线程中修改了标志位"); }}//打印若干t1中的打印语句之后,主线程main中修改标志位之后,可以终止t1
注意:上述优化现象只会出现在频繁读的情况,如果不是频繁读,就不会出现那样的优化.
指令重排序问题
生活案例:买菜
如果是傻乎乎的按照菜单从上到下的去买菜,从路线图可以看出,不必要的路是真的没少走.
如果执行代码时,编译器认为某些个代码调整一下顺序并不会影响结果,那代码的执行顺序就会被调整,就比如可以把上面买菜的顺序调整成:黄瓜->萝卜->青菜->茄子
单线程这样的指令重排一般不会出现问题,但是多线程并发时,还这样优化,就容易出现问题
针对这样的问题,如果是针对一个变量,我们可以使用volatile修饰,如果是针对代码块,我们可以使用synchronized.
synchronized的用法
synchronized起作用的本质
修饰普通方法
修饰静态方法
修饰代码块
synchronized起作用的本质
因为我们知道java中所有类都继承了Object,所以所有类都包含了Object的部分,我们可以称这继承的部分是"对象头",使用synchronized进行对象头中的标志位的修改,就可以做到一个对象的锁一个时刻只能被一个线程所持有,其他线程此时不可抢占.这样的设置,就好像把一个对象给锁住了一样.
修饰普通方法
如前述两个线程给同一个count进行自增的案例.不再赘述.此时的所对象就是Counter对象
修饰静态方法⚡️
与普通方法类似.只不过这个方法可以类名直接调用.
修饰代码块
首先修饰代码块需要执行锁对象是谁,所以这里可以分为三类,一个是修饰普通方法的方法体这个代码块的写法,其次是修饰静态方法方法体的写法,最后可以单独写一个Object的对象,来对这个Object对象进行上锁.
class Counter{ private int count; public void increase(){ synchronized(this){ count++; } } public int getCount(){ return this.count; }}class Counter{ private static int count; public static void increase(){ synchronized(Counter.class){//注意这里锁的是类对象哦 count++; } } public int getCount(){ return this.count; }}class Counter{ private static int count; private static Object locker=new Object(); public static void increase(){ synchronized(locker){ count++; } } public int getCount(){ return this.count; }}
注意:java中这种随手拿一个对象就能上锁的用法,是java中一种很有特色的用法,在别的语言中,都是有专门的锁对象的.
Conclusion
java中的线程状态,以及如何区分线程安全问题 罪恶之源是抢占式执行多线程对同一个变量进行修改,多线程只读一个变量是没有线程安全问题的修改操作是非原子性的内存可见性引起的线程安全问题指令重排序引起的线程安全问题 synchronized的本质和用法
1.java中的线程状态,以及如何区分
2.线程安全问题
罪恶之源是抢占式执行
多线程对同一个变量进行修改,多线程只读一个变量是没有线程安全问题的
修改操作是非原子性的
内存可见性引起的线程安全问题
指令重排序引起的线程安全问题
3.synchronized的本质和用法
关于"Java中线程状态+线程安全问题+synchronized的用法是什么"这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对"Java中线程状态+线程安全问题+synchronized的用法是什么"知识都有一定的了解,大家如果还想学习更多知识,欢迎关注行业资讯频道。
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.