How to solve thread safety issues

Transfer: https://www.cnblogs.com/dolphin0520/p/3923737.html

While multi-threaded programming greatly improve the efficiency, but will also bring some risks. For example, two threads simultaneously inserting a unique data into a database table, it may cause the database to insert the same data. Today we'll discuss next thread safety issues, as well as what Java provides a mechanism to solve thread safety issues.

  The following is an outline of this directory:

  A. When will thread safety problems?

  II. How to solve thread safety issues?

  Three sync blocks or synchronization method .synchronized

  If not correct place, a lot of understanding and welcome criticism.

  Please respect the author fruits of labor, reproduced, please indicate the original link:

  http://www.cnblogs.com/dolphin0520/p/3923737.html

A. When will thread safety problems?

  Do not appear in a single thread thread safety issues, and in multi-threaded programming, there may be a case of simultaneous access to the same resources will appear, this resource may be various types of resources: a variable, an object, a file , such as a database table, and when multiple threads access the same resources, will there is a problem:

  As the process for each thread of execution can not be controlled, it is likely to lead to the end result and in fact contrary to the desire to direct or cause the program error.

  Here is a simple example:

  Now there are two threads to read data from the network, and then inserted into a database table, it is required not insert duplicate data.

  So there must be two operations during insertion data:

  1) checking whether there piece of data in the database;

  2) If there is no intervening; if not, then inserted into the database.

  If two threads with a thread-1, respectively, and thread-2 represents, a time, and a thread-1 are thread-2 to read data X, then this situation may occur:

  Whether there is data X thread-1 database to check, and then to thread-2 also checks whether there is data in the database X.

  Results are two threads inspection data X does not exist in the database, then the two threads are, respectively, among the data X into a database table.

  This is a thread safety problem in that multiple threads simultaneously access a resource, the program will lead to results not want to see the results of running.

  There, the resources are known as: critical resources (also known as shared resources).

  That is, when multiple threads access the critical resource (object, object attribute, a file, a database, etc.), it may produce thread-safety issues.

  However, when multiple threads execute a method, local variable inside the method is not critical resources, because the method is performed on the stack, and the Java thread stack is private, it does not produce thread-safety issues.

II. How to solve thread safety issues?

  So generally speaking, it is how to solve thread safety problems?

  Basically all of the concurrent model in solving security thread, use the "serialize access to critical resources" program, which at the same time, only one thread access to critical resources, also known as synchronous exclusive access.

  Typically, it is in front of the code to access critical resources plus a lock, when the lock release complete access to critical resources, so that other threads continue to access.

  In Java, it provides two ways to synchronize exclusive access: synchronized and Lock.

  This paper describes the use of synchronized, Lock to use about a blog post next.

Three sync blocks or synchronization method .synchronized

  Before looking at the use of the synchronized keyword, we first look at a concept: mutex, as the name implies: to be able to reach the lock exclusive access purposes.

  Here is a simple example: If you add mutex for critical resources, when a thread when accessing the critical resource, other threads will have to wait.

  In Java, every object has a lock mark (monitor), also known as the monitor, when multiple threads simultaneously access an object, thread only get a lock to access the object.

  In Java, you can use the synchronized keyword to mark a method or block of code, when a thread calls the synchronized method or object code block access synchronized, this thread will get the lock of the object, other threads temporarily unable to access this method, this method is only waiting for the code block is finished or finished, this thread will release the lock of the object, other threads to execute the method or block of code.

  Synchronized keyword below to illustrate the use of a few simple examples:

  1.synchronized方法

  The following code calls insertData two threads are inserted in the data objects:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public  class  Test {
 
     public  static  void  main(String[] args)  {
         final  InsertData insertData =  new  InsertData();
         
         new  Thread() {
             public  void  run() {
                 insertData.insert(Thread.currentThread());
             };
         }.start();
         
         
         new  Thread() {
             public  void  run() {
                 insertData.insert(Thread.currentThread());
             };
         }.start();
     }  
}
 
class  InsertData {
     private  ArrayList<Integer> arrayList =  new  ArrayList<Integer>();
     
     public  void  insert(Thread thread){
         for ( int  i= 0 ;i< 5 ;i++){
             System.out.println(thread.getName()+ "在插入数据" +i);
             arrayList.add(i);
         }
     }
}

  At this time, the output of the program is:

  

  Description two threads simultaneously executing the insert method.

  And if coupled with synchronized keyword in front of the insert method, then run the result is:

1
2
3
4
5
6
7
8
9
10
class  InsertData {
     private  ArrayList<Integer> arrayList =  new  ArrayList<Integer>();
     
     public  synchronized  void  insert(Thread thread){
         for ( int  i= 0 ;i< 5 ;i++){
             System.out.println(thread.getName()+ "在插入数据" +i);
             arrayList.add(i);
         }
     }
}

  

  The output from the description, Thread-1 is inserted the like Thread-0 data only be carried out after completion of the insertion data. Description Thread-0 and Thread-1 is performed sequentially insert method.

  This is the synchronized method.

  However, there are several points to note:

  1)当一个线程正在访问一个对象的synchronized方法,那么其他线程不能访问该对象的其他synchronized方法。这个原因很简单,因为一个对象只有一把锁,当一个线程获取了该对象的锁之后,其他线程无法获取该对象的锁,所以无法访问该对象的其他synchronized方法。

  2)当一个线程正在访问一个对象的synchronized方法,那么其他线程能访问该对象的非synchronized方法。这个原因很简单,访问非synchronized方法不需要获得该对象的锁,假如一个方法没用synchronized关键字修饰,说明它不会使用到临界资源,那么其他线程是可以访问这个方法的,

  3)如果一个线程A需要访问对象object1的synchronized方法fun1,另外一个线程B需要访问对象object2的synchronized方法fun1,即使object1和object2是同一类型),也不会产生线程安全问题,因为他们访问的是不同的对象,所以不存在互斥问题。

  2.synchronized代码块

  synchronized代码块类似于以下这种形式:

1
2
3
synchronized (synObject) {
         
     }

  当在某个线程中执行这段代码块,该线程会获取对象synObject的锁,从而使得其他线程无法同时访问该代码块。

  synObject可以是this,代表获取当前对象的锁,也可以是类中的一个属性,代表获取该属性的锁。

  比如上面的insert方法可以改成以下两种形式:

1
2
3
4
5
6
7
8
9
10
11
12
class  InsertData {
     private  ArrayList<Integer> arrayList =  new  ArrayList<Integer>();
     
     public  void  insert(Thread thread){
         synchronized  ( this ) {
             for ( int  i= 0 ;i< 100 ;i++){
                 System.out.println(thread.getName()+ "在插入数据" +i);
                 arrayList.add(i);
             }
         }
     }
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
class  InsertData {
     private  ArrayList<Integer> arrayList =  new  ArrayList<Integer>();
     private  Object object =  new  Object();
     
     public  void  insert(Thread thread){
         synchronized  (object) {
             for ( int  i= 0 ;i< 100 ;i++){
                 System.out.println(thread.getName()+ "在插入数据" +i);
                 arrayList.add(i);
             }
         }
     }
}

  从上面可以看出,synchronized代码块使用起来比synchronized方法要灵活得多。因为也许一个方法中只有一部分代码只需要同步,如果此时对整个方法用synchronized进行同步,会影响程序执行效率。而使用synchronized代码块就可以避免这个问题,synchronized代码块可以实现只对需要同步的地方进行同步。

  另外,每个类也会有一个锁,它可以用来控制对static数据成员的并发访问。

  并且如果一个线程执行一个对象的非static synchronized方法,另外一个线程需要执行这个对象所属类的static synchronized方法,此时不会发生互斥现象,因为访问static synchronized方法占用的是类锁,而访问非static synchronized方法占用的是对象锁,所以不存在互斥现象。

看下面这段代码就明白了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public  class  Test {
 
     public  static  void  main(String[] args)  {
         final  InsertData insertData =  new  InsertData();
         new  Thread(){
             @Override
             public  void  run() {
                 insertData.insert();
             }
         }.start(); 
         new  Thread(){
             @Override
             public  void  run() {
                 insertData.insert1();
             }
         }.start();
     }  
}
 
class  InsertData { 
     public  synchronized  void  insert(){
         System.out.println( "执行insert" );
         try  {
             Thread.sleep( 5000 );
         catch  (InterruptedException e) {
             e.printStackTrace();
         }
         System.out.println( "执行insert完毕" );
     }
     
     public  synchronized  static  void  insert1() {
         System.out.println( "执行insert1" );
         System.out.println( "执行insert1完毕" );
     }
}

  执行结果;

  

  第一个线程里面执行的是insert方法,不会导致第二个线程执行insert1方法发生阻塞现象。

  下面我们看一下synchronized关键字到底做了什么事情,我们来反编译它的字节码看一下,下面这段代码反编译后的字节码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public  class  InsertData {
     private  Object object =  new  Object();
     
     public  void  insert(Thread thread){
         synchronized  (object) {
         
         }
     }
     
     public  synchronized  void  insert1(Thread thread){
         
     }
     
     public  void  insert2(Thread thread){
         
     }
}

  

  从反编译获得的字节码可以看出,synchronized代码块实际上多了monitorenter和monitorexit两条指令。monitorenter指令执行时会让对象的锁计数加1,而monitorexit指令执行时会让对象的锁计数减1,其实这个与操作系统里面的PV操作很像,操作系统里面的PV操作就是用来控制多个线程对临界资源的访问。对于synchronized方法,执行中的线程识别该方法的 method_info 结构是否有 ACC_SYNCHRONIZED 标记设置,然后它自动获取对象的锁,调用方法,最后释放锁。如果有异常发生,线程自动释放锁。

  

  有一点要注意:对于synchronized方法或者synchronized代码块,当出现异常时,JVM会自动释放当前线程占用的锁,因此不会由于异常导致出现死锁现象。

 

  参考资料:

  《Java编程思想》

  http://ifeve.com/synchronized-blocks/

  http://ifeve.com/java-synchronized/

  http://blog.csdn.net/ns_code/article/details/17199201

Guess you like

Origin www.cnblogs.com/wjqhuaxia/p/11741821.html