synchronize and use of the principle of

synchronize use

We know that when the race condition occurs, the application will not be synchronized.

for example:

package sync;

import org.junit.Test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

import static org.junit.Assert.assertEquals;

public class OceanSyncMethods {
    private int sum = 0;

    public void setSum(int sum) {
        this.sum = sum;
    }

    public int getSum() {
        return sum;
    }
    public void calculate(){
        setSum(getSum()+1);
    }

    @Test
    public void givenMultiThread_whenNonSyncMethod() throws InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(3);
        OceanSyncMethods summation = new OceanSyncMethods();

        IntStream.range(0,1000)
                .forEach(count -> service.submit(summation::calculate));
        service.awaitTermination(1000, TimeUnit.MILLISECONDS);

        assertEquals(1000,summation.getSum());
    }


}

We have a thread pool, which fixed three threads. These threads execute calculate method 1000.

It stands to reason, summation.getSum()it will be 1000, however, if there is a thread safety problem, then some values will be lost.

Test results are often not 1000:

java.lang.AssertionError: 
Expected :1000
Actual   :997

Of course, we can synchronize keywords to complete the synchronization.

  • Examples of methods for synchronize
   public synchronized void calculate(){
        setSum(getSum()+1);
    }

Since it is an instance method to lock, we must know who is locked in the body. Examples ultimately (Summation) calculate method is invoked, so that the lock instance (object). It means that only one instance of a class for each thread can perform the method.

  • synchronize static method
package sync;

import org.junit.Test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

import static org.junit.Assert.assertEquals;

public class OceanSyncMethods {
    private static int staticSum = 0;

    public static synchronized void calculate(){
        staticSum = staticSum + 1;
    }

    @Test
    public void givenMultiThread_whenNonSyncMethod() throws InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(3);


        IntStream.range(0,1000)
                .forEach(count -> service.submit(OceanSyncMethods::calculate));
        service.awaitTermination(5000, TimeUnit.MILLISECONDS);

        assertEquals(1000,OceanSyncMethods.staticSum);
    }


}

Lock on a static method is also a lock, then the lock at this time who is it?

Lock is a class object. Because the JVM a class has only one class object, so that each class has only one execution thread can synchronize the code in a static method.

  • synchronize blocks within a method
package sync;

import org.junit.Test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

import static org.junit.Assert.assertEquals;

public class OceanSyncMethods {
   private int count;

    public void setCount(int count) {
        this.count = count;
    }

    public int getCount() {
        return count;
    }

    public void performSynchronizedTask(){
        synchronized (this){
            setCount(getCount()+1);
        }
    }

    @Test
    public void givenMultiThread_whenNonSyncMethod() throws InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(3);
        OceanSyncMethods oceanSyncMethods = new OceanSyncMethods();

        IntStream.range(0,1000)
                .forEach(count -> service.submit(oceanSyncMethods::performSynchronizedTask));
        service.awaitTermination(5000, TimeUnit.MILLISECONDS);

        assertEquals(1000,oceanSyncMethods.getCount());
    }


}

Sometimes lock an entire method is not efficient, we need to have real security concerns thread can lock code, and therefore have a synchronize code blocks.

Above we lock is this, that this is a monitor object. Each monitor object can execute only one thread synchronize the contents of the code blocks.

So what is a monitor it? monitor is like a building, synchronize block of code is like a special room, monitor ensures that only one thread to enter this special room, in order to protect data.

Note that if the method is static, then lock class object. At this time, the class will become a monitor.

package sync;

import org.junit.Test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

import static org.junit.Assert.assertEquals;

public class OceanSyncMethods {
    private static int count = 0;

    public static void performSynchronizedTask() {
        synchronized (OceanSyncMethods.class) {
            count = count+1;
        }
    }

    @Test
    public void givenMultiThread_whenNonSyncMethod() throws InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(3);


        IntStream.range(0, 1000)
                .forEach(count -> service.submit(OceanSyncMethods::performSynchronizedTask));
        service.awaitTermination(5000, TimeUnit.MILLISECONDS);

        assertEquals(1000,OceanSyncMethods.count );
    }


}

synchronize principle

For example, the lock object, then, is how locked?

We do not lock on there is definitely identified as the flag the same thing. For example, an object is locked, and what does it change?

maven project was added dependent on:

<dependency>
        <groupId>org.openjdk.jol</groupId>
        <artifactId>jol-core</artifactId>
        <version>0.9</version>
    </dependency>

For simplicity, our class will be extremely simple. First, to build its own lock object (because we want to observe the state of the object).

package sync;

public class MyLock {
    boolean flag = true;
}

test:

package sync;

import org.openjdk.jol.info.ClassLayout;

public class TestSync {
    static MyLock myLock = new MyLock();

    static void somePrint(){
        synchronized (myLock){
            System.out.println("Nothing important in the sync block");
        }
    }

    public static void main(String[] args) {

        System.out.println(Integer.toHexString(myLock.hashCode()));
        System.out.println(ClassLayout.parseInstance(myLock).toPrintable());

        somePrint();


    }
}

We synchronize with the embodiment of the locking block, and the lock is MyLock object.

We print the hashcode myLock in the main method, the object layout and print:

45ee12a7
sync.MyLock object internals:
 OFFSET  SIZE      TYPE DESCRIPTION                               VALUE
      0     4           (object header)                           01 a7 12 ee (00000001 10100111 00010010 11101110) (-300767487)
      4     4           (object header)                           45 00 00 00 (01000101 00000000 00000000 00000000) (69)
      8     4           (object header)                           18 0a 89 14 (00011000 00001010 10001001 00010100) (344525336)
     12     1   boolean MyLock.flag                               true
     13     3           (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total

Nothing important in the sync block

Process finished with exit code 0

instance size is 16bytes, it must be a multiple of 8 (64 in a virtual machine).

This object consists of three parts: the first objects, variables, data alignment.

That variable is a Boolean value, accounting for 1byte. Its data accounting 3bytes. object header accounted for 12bytes, which is 96bits.

What is the subject header?

object header
Common structure at the beginning of every GC-managed heap object. (Every oop points to an object header.) Includes fundamental information about the heap object’s layout, type, GC state, synchronization state, and identity hash code. Consists of two words. In arrays it is immediately followed by a length field. Note that both Java objects and VM-internal objects have a common object header format.

Object head, is the general structure of the beginning of each heap object gc managed.

oop is a pointer to the object header.

Object header comprising a stack layout object, the type, GC state, the synchronization state, and identityHashcode.

It consists of two word components.

Which two word it?

mark word
The first word of every object header. Usually a set of bitfields including synchronization state and identity hash code. May also be a pointer (with characteristic low bit encoding) to synchronization related information. During GC, may contain GC state bits.

klass pointer
The second word of every object header. Points to another object (a metaobject) which describes the layout and behavior of the original object. For Java objects, the “klass” contains a C++ style “vtable”

One is the mark word, one is klass pointer.

We are looking for the lock on state variables, in mark word in.


In 64 the JVM, mark word accounting 64bits, klass pointer accounted for 64bits.

But we just print in, klass pointer only a 32bits, since opened pointer compression.

We are concerned about mark word in the normal state.

There is 32bits hashcode, and we print hashcode is the same.

However, it is from the back counting down.

There 4bits age. Heap is segmented age, it was not in the area of ​​two survivor moved to remove it? To the age of fifteen I went to old age. So where is the age record something in it?

Yes, that's the mark word that 4bits, the maximum is 15 years old 4bits represented.

There 1bit identify biased locking.

The remaining 2bits we are looking for state of the lock.


FIG above well illustrated mark word representing the layout, and the status of different objects.

Object has five states:

General, tend to lock, lock lightweight, heavyweight lock for gc mark

Right part of the picture shows the standard locking process.

As long as an object is not locked, the final value of the two is 01. When a way to lock an object, object corresponding information in the header will lock record exists in the current stack frame.

Then the virtual machine will try to load the mark word pointing to lock record pointer, use the cas operation. If successful, the current thread will be locked immediately. Since the lock records are always aligned in the word's edge, mark word the last two will become 00, marking the object being locked.

If it had a lock on the object before, resulting in cas operation fails, the virtual method to check mark whether the word refers to the current thread's stack. If this is the case, it means that this thread has possession of the object and can proceed safely. For objects such iteration locked, lock record the initial value of 0 is not the mark word of. Only when the two threads must present the same object lock, the lock will expand into a lightweight heavyweight lock in order to manage the waiting thread.

Heavyweight locks to lock lightweight inexpensive compared, but their performance will be affected - cas each operation must be executed atomically on multicore processors, each object even if only because locking and unlocking a particular the rout.

In java6, this shortcoming called into so-called " free storage biased locking technology ." Only the first acquire a lock when it is executed atomic cas operation to the load lock thread ID to mark word in. This object is therefore said to be biased to the thread (discrimination) of. Future for locking and unlocking the object with a thread is no longer required atomic operations, or to update the mark word. Even lock the record on the stack will not be initialized, because it will never be detected as a deviation object.


Reference: https: //wiki.openjdk.java.net/display/HotSpot/Synchronization

Published 25 original articles · won praise 26 · views 1130

Guess you like

Origin blog.csdn.net/weixin_43810802/article/details/104107860