Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is thread safety and ThreadGroup

2025-03-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly introduces "what is thread safety and ThreadGroup". In daily operation, I believe that many people have doubts about what is thread safety and ThreadGroup. The editor consulted all kinds of materials and sorted out simple and easy-to-use methods of operation. I hope it will be helpful to answer the doubts of "what is thread safety and ThreadGroup"! Next, please follow the editor to study!

Brief introduction

Synchronized can prevent thread interference and memory consistency errors, as shown below:

Synchronized provides a locking mechanism that ensures mutually exclusive access to shared variables, thus preventing data inconsistencies.

Synchronized includes two JVM instructions monitor enter and monitor exit, which can guarantee that any thread must get data from main memory, not from cache, at any time before monitor enter is successfully executed. After monitor exit runs successfully, the updated value of the shared variable must be brushed into main memory instead of just in the cache.

Synchronized instructions strictly follow the Happens-Beofre rules. A monitor exit instruction must be preceded by a monitor enter.

Basic usage

The basic usage of synchronized can be used to modify code blocks or methods, such as:

Private final Object MUTEX = new Object (); public void sync1 () {synchronized (MUTEX) {}} public synchronized void sync2 () {} bytecode simple analysis

A simple example is as follows:

Public class Main {private static final Object MUTEX = new Object (); public static void main (String [] args) throws InterruptedException {final Main m = new Main (); for (int I = 0; I

< 5; i++) { new Thread(m::access).start(); } } public void access(){ synchronized (MUTEX){ try{ TimeUnit.SECONDS.sleep(20); }catch (InterruptedException e){ e.printStackTrace(); } } }} 编译后查看字节码: javap -v -c -s -l Main.class access()字节码截取如下: stack=3, locals=4, args_size=1 0: getstatic #9 // Field MUTEX:Ljava/lang/Object; 获取MUTEX 3: dup 4: astore_1 5: monitorenter // 执行monitor enter指令 6: getstatic #10 // Field java/util/concurrent/TimeUnit.SECONDS:Ljava/util/concurrent/TimeUnit; 9: ldc2_w #11 // long 20l12: invokevirtual #13 // Method java/util/concurrent/TimeUnit.sleep:(J)V15: goto 23 // 正常退出,跳转到字节码偏移量23的地方18: astore_219: aload_220: invokevirtual #15 // Method java/lang/InterruptedException.printStackTrace:()V23: aload_124: monitorexit // monitor exit指令25: goto 3328: astore_329: aload_130: monitorexit31: aload_332: athrow33: return 关于monitorenter与monitorexit说明如下: monitorenter:每一个对象与一个monitor相对应,一个线程尝试获取与对象关联的monitor的时候,如果monitor的计数器为0,会获得之后立即对计数器加1,如果一个已经拥有monitor所有权的线程重入,将导致计数器再次累加,而如果其他线程尝试获取时,会一直阻塞直到monitor的计数器变为0,才能再次尝试获取对monitor的所有权 monitorexit:释放对monitor的所有权,将monitor的计数器减1,如果计数器为0,意味着该线程不再拥有对monitor的所有权 注意事项非空对象 与monitor关联的对象不能为空: private Object MUTEX = null;private void sync(){ synchronized (MUTEX){ }} 会直接抛出空指针异常。 作用域不当 由于synchronized关键字存在排它性,作用域越大,往往意味着效率越低,甚至丧失并发优势,比如: private synchronized void sync(){ method1(); syncMethod(); method2();} 其中只有第二个方法是并发操作,那么可以修改为 private Object MUTEX = new Object();private void sync(){ method1(); synchronized (MUTEX){ syncMethod(); } method2();}使用不同的对象 因为一个对象与一个monitor相关联,如果使用不同的对象,这样就失去了同步的意义,例子如下: public class Main { public static class Task implements Runnable{ private final Object MUTEX = new Object(); @Override public void run(){ synchronized (MUTEX){ } } } public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 20; i++) { new Thread(new Task()).start(); } }} 每一个线程争夺的monitor都是互相独立的,这样就失去了同步的意义,起不到互斥的作用。 死锁 另外,使用synchronized还需要注意的是有可能造成死锁的问题,先来看一下造成死锁可能的原因。 死锁成因 交叉锁导致程序死锁:比如线程A持有R1的锁等待R2的锁,线程B持有R2的锁等待R1的锁 内存不足:比如两个线程T1和T2,T1已获取10MB内存,T2获取了15MB内存,T1和T2都需要获取30MB内存才能工作,但是剩余可用的内存为10MB,这样两个线程都在等待彼此释放内存资源 一问一答式的数据交换:服务器开启某个端口,等待客户端访问,客户端发送请求后,服务器因某些原因错过了客户端请求,导致客户端等待服务器回应,而服务器等待客户端发送请求 死循环引起的死锁:比较常见,使用jstack等工具看不到死锁,但是程序不工作,CPU占有率高,这种死锁也叫系统假死,难以排查和重现 例子public class Main { private final Object MUTEX_READ = new Object(); private final Object MUTEX_WRITE = new Object(); public void read(){ synchronized (MUTEX_READ){ synchronized (MUTEX_WRITE){ } } } public void write(){ synchronized (MUTEX_WRITE){ synchronized (MUTEX_READ){ } } } public static void main(String[] args) throws InterruptedException { Main m = new Main(); new Thread(()->

{while (true) {m.read ();}}) .start (); new Thread (()-> {while (true) {m.write ();}}) .start ();}}

Two threads own the MUTEX_READ/MUTEX_WRITE while waiting for another thread to release the MUTEX_WRITE/MUTEX_READ, which is the deadlock caused by the cross-lock.

Investigation

After finding the process using jps, view it through jstack:

You can see a clear hint that a deadlock was found, Thread-0 waiting for the monitor possessed by Thread-1, and Thread-1 waiting for the monitor possessed by Thread-0.

Two special monitor

Here are two special monitor:

This monitor

Class monitor

This monitor

Let's start with a piece of code:

Public class Main {public synchronized void method1 () {System.out.println (Thread.currentThread (). GetName () + "method1"); try {TimeUnit.MINUTES.sleep (5);} catch (InterruptedException e) {e.printStackTrace ();}} public synchronized void method2 () {System.out.println (Thread.currentThread (). GetName () + "method2") Try {TimeUnit.MINUTES.sleep (5);} catch (InterruptedException e) {e.printStackTrace ();}} public static void main (String [] args) throws InterruptedException {Main m = new Main (); new Thread (m::method1). Start (); new Thread (m::method2). Start ();}}

After running, you can find that there is only one line of output, that is, only one of the methods has been run, and the other method has not been executed at all, which can be found using jstack:

One thread is dormant while the other thread is blocked. If you modify method2 () as follows:

Public void method2 () {synchronized (this) {System.out.println (Thread.currentThread (). GetName () + "method2"); try {TimeUnit.MINUTES.sleep (5);} catch (InterruptedException e) {e.printStackTrace ();}

The effect is the same. That is, using synchronized on a method is equivalent to synchronized (this).

Class monitor

Change the method in the above code to a static method:

Public class Main {public static synchronized void method1 () {System.out.println (Thread.currentThread (). GetName () + "method1"); try {TimeUnit.MINUTES.sleep (5);} catch (InterruptedException e) {e.printStackTrace ();}} public static synchronized void method2 () {System.out.println (Thread.currentThread (). GetName () + "method2") Try {TimeUnit.MINUTES.sleep (5);} catch (InterruptedException e) {e.printStackTrace ();}} public static void main (String [] args) throws InterruptedException {new Thread (Main::method1). Start (); new Thread (Main::method2). Start ();}}

After running it, you can see that the output is still only one line, that is, only one of the methods is run, and the jstack analysis is similar:

If you modify method2 () as follows:

Public static void method2 () {synchronized (Main.class) {System.out.println (Thread.currentThread (). GetName () + "method2"); try {TimeUnit.MINUTES.sleep (5);} catch (InterruptedException e) {e.printStackTrace ();}

You can find that the output is still consistent, that is, synchronized on static methods is equivalent to synchronized (XXX.class).

Summary

This monitor: synchronized on a member method, which is this monitor, which is equivalent to using synchronized (this) in a method

Class monitor: synchronized on static methods, or class monitor, is equivalent to using synchronized (XXX.class) in static methods

Introduction to ThreadGroup

In any case, a newly created thread is added to a ThreadGroup:

If the new thread does not specify a ThreadGroup, the default is the ThreadGroup where the main thread resides

If ThreadGroup is specified, then join the ThreadGroup

There is a parent-child relationship in ThreadGroup, and a ThreadGroup can have a child ThreadGroup.

Create

You can create a ThreadGroup directly through the constructor. There are two constructors: one is to specify the name directly (ThreadGroup is the ThreadGroup of the main thread), and the other is the constructor with the parent ThreadGroup and name:

ThreadGroup group1 = new ThreadGroup ("name"); ThreadGroup group2 = new ThreadGroup (group1, "name2")

Complete example:

Public static void main (String [] args) throws InterruptedException {ThreadGroup group1 = new ThreadGroup ("name"); ThreadGroup group2 = new ThreadGroup (group1, "name2"); System.out.println (group2.getParent () = = group1); System.out.println (group1.getParent (). GetName ());}

Output result:

Truemainenumerate ()

Enumerate () can be used for Thread and ThreadGroup replication, because a ThreadGroup can add several Thread and several sub-ThreadGroup, using this method can be easily replicated. The method is described as follows:

Public int enumerate (Thread [] list)

Public int enumerate (Thread [] list, boolean recurse)

Public int enumerate (ThreadGroup [] list)

Public int enumerate (ThreadGroup [] list, boolean recurse)

The above method copies the active thread / ThreadGroup in ThreadGroup into the Thread/ThreadGroup array, and the Boolean parameter indicates whether recursive replication is enabled.

Examples are as follows:

Public static void main (String [] args) throws InterruptedException {ThreadGroup myGroup = new ThreadGroup ("MyGroup"); Thread thread = new Thread (myGroup, ()-> {while (true) {try {TimeUnit.SECONDS.sleep (1);} catch (InterruptedException e) {e.printStackTrace ();}, "MyThread"); thread.start () TimeUnit.MILLISECONDS.sleep (1); ThreadGroup mainGroup = currentThread (). GetThreadGroup (); Thread [] list = new Thread [mainGroup.activeCount ()]; int recurseSize = mainGroup.enumerate (list); System.out.println (recurseSize); recurseSize = mainGroup.enumerate (list,false); System.out.println (recurseSize);}

The latter output is 1 less than the previous one because it does not contain threads in myGroup (recursively set to false). It should be noted that the thread obtained by enumerate () is only an estimate, and there is no 100% guarantee that the active thread of the current group. For example, after calling replication, a thread ends its life cycle or a new thread joins in, which will lead to inaccurate data. In addition, the int value returned is more realistic than the length of Thread [], because enumerate only puts the currently active threads into an array, while the return value int represents the real number rather than the length of the array.

Other API

ActiveCount (): gets the active thread in group, estimated value

ActiveGroupCount (): gets the active sub-group in group, which is also an approximate value, and recursively gets all the sub-group.

GetMaxPriority (): used to get the priority of group. By default, the priority of group is 10, and the priority of all threads must not be greater than the priority of the group where the thread resides.

GetName (): get the group name

GetParent (): gets the parent group, and returns null if it does not exist

List (): an output method that recursively outputs all active thread information to the console

ParentOf (ThreadGroup g): determines whether the current group is the parent group of a given group, and returns true if the given group is itself.

SetMaxPriority (int pri): specify the maximum priority of group. After setting it, it will also change the maximum priority of all sub-group. In addition, after changing the priority, the thread priority will be higher than the group priority. For example, if the thread priority is 10 and the group priority is set to 5, the thread priority will be greater than the group priority, but the newly added thread priority must not be greater than the group priority.

Interrupt (): causes all active threads to be interrupted, recursively invokes the thread's interrupt ()

Destroy (): if there are no active threads, remove yourself from the parent group after the call

SetDaemon (boolean daemon): when set to guardian ThreadGroup, if the ThreadGroup does not have any active threads, it is automatically destroyed

At this point, the study on "what is thread safety and ThreadGroup" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

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.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report