1、Synchronized锁

2、Lock锁

synchronized锁

synchronized是Java的一个关键字,它能够将代码块(方法)锁起来。只要在代码块(方法)添加关键字synchronized,即可以实现同步的功能。

public synchronized void test() {    // doSomething}

特征

1、synchronized是一种互斥锁。

一次只能允许一个线程进入被锁住的代码块。

2、synchronized是一种内置锁/监视器锁。

Java中每个对象都有一个内置锁(监视器,也可以理解成锁标记),而synchronized就是使用对象的内置锁(监视器)来将代码块(方法)锁定的!

用处

1、synchronized保证了线程的原子性。

被保护的代码块是一次被执行的,没有任何线程会同时访问;2、synchronized还保证了可见性。

当执行完synchronized之后,修改后的变量对其他的线程是可见的;

3、Java中的synchronized,通过使用内置锁,来实现对变量的同步操作,进而实现了对变量操作的原子性和其他线程对变量的可见性,从而确保了并发情况下的线程安全。

使用

synchronized一般用来修饰三种东西:修饰普通方法;修饰代码块;修饰静态方法;

1、修饰普通方法用的锁是MainSyn对象(内置锁)

public class MainSyn {    /**     * 修饰普通方法,此时用的锁是MainSyn对象(内置锁)     */    public synchronized void test() {        // doSomething    }}

2、修饰代码块用的锁是MainSyn对象(内置锁)--->this

public class MainSyn {    public void test() {        // 修饰代码块,此时用的锁是MainSyn对象(内置锁)--->this        synchronized (this) {            // doSomething        }    }}

当然了,使用synchronized修饰代码块时未必使用this,还可以使用其他的对象(随便一个对象都有一个内置锁)

public class MainSyn {    /**     * 使用object作为锁(任何对象都有对应的锁标记,object也不例外)     */    private final Object object = new Object();    public void test() {        // 修饰代码块,此时用的锁是自己创建的锁Object        synchronized (object) {            // doSomething        }    }}

3、修饰静态方法获取到的是类锁(类的字节码文件对象):MainSyn.class

public class MainSyn {    /**     * 修饰静态方法代码块,静态方法属于类方法,它属于这个类。     * 获取到的锁是属于类的锁(类的字节码文件对象)-->MainSyn.class     */    public static synchronized void test() {        // doSomething    }}

类锁与对象锁

synchronized修饰静态方法获取的是类锁(类的字节码文件对象),synchronized修饰普通方法或代码块获取的是对象锁。

它俩是不冲突的,也就是说:获取了类锁的线程和获取了对象锁的线程是不冲突的!

public class SynchronizedDemo {    /**     * synchronized修饰非静态方法     */    public synchronized void function() throws InterruptedException {        for (int i = 0; i < 3; i++) {            Thread.sleep(1000);            System.out.println("function running...");        }    }    /**     * synchronized修饰静态方法     */    public static synchronized void staticFunction() throws InterruptedException {        for (int i = 0; i < 3; i++) {            Thread.sleep(1000);            System.out.println("staticFunction running...");        }    }    public static void main(String[] args) {        final SynchronizedDemo demo = new SynchronizedDemo();        // 创建线程执行静态方法        Thread t1 = new Thread(() -> {            try {                staticFunction();            } catch (InterruptedException e) {                e.printStackTrace();            }        });        // 创建线程执行实例方法        Thread t2 = new Thread(() -> {            try {                demo.function();            } catch (InterruptedException e) {                e.printStackTrace();            }        });        // 启动线程        t1.start();        t2.start();    }}

测试结果:类锁和对象锁是不会冲突的。

java method上的synchronized的锁 java synchronized是什么锁_代码块

重入锁

看下面的代码:

public class Widget {    /**     * 锁住了     */    public synchronized void doSomething() {        System.out.println("Widget:calling doSomething");    }}
public class LoggingWidget extends Widget {    /**     * 锁住了     */    @Override    public synchronized void doSomething() {        System.out.println("LoggingWidget:calling doSomething");        super.doSomething();    }}

1、当线程A进入到LoggingWidget的doSomething()方法时,此时拿到了LoggingWidget实例对象的锁。2、随后在方法上又调用了父类Widget的doSomething()方法,它又是被synchronized修饰。3、那现在LoggingWidget实例对象的锁还没有释放,进入父类Widget的doSomething()方法还需要一把锁吗?不需要的!

因为锁的持有者是“线程”,而不是“调用”。线程A已经是有了LoggingWidget实例对象的锁了,当再需要的时候可以继续“开锁”进去的!这就是内置锁的可重入性。

释放锁的时机

1、当方法(代码块)执行完毕后会自动释放锁,不需要做任何的操作。

2、当一个线程执行的代码出现异常时,其所持有的锁会自动释放。

不会由于异常导致出现死锁现象。

原理

看下synchronized修饰方法和代码块的代码:

public class SynchronizedDemo {    /**     *  修饰方法     */    public synchronized void test1() {        // doSomething    }    public void test2() {        // 修饰代码块        synchronized (this) {                    }    }}

来反编译看一下javap -c SynchronizedDemo:

java method上的synchronized的锁 java synchronized是什么锁_代码块_02

根据上面的反编译的图片可以看出:

1、同步代码块:

monitorenter和monitorexit指令实现的。

2、同步方法(在这看不出来需要看JVM底层实现):

方法修饰符上的ACC_SYNCHRONIZED实现。

synchronized底层是是通过monitor对象,对象有自己的对象头,存储了很多信息,其中一个信息标示是被哪个线程持有。

Lock锁

Lock显式锁是JDK1.5之后才有的,之前都是使用Synchronized锁来使线程安全的。

Lock显式锁是一个接口

java method上的synchronized的锁 java synchronized是什么锁_sed_03

看下Lock这个接口的注释说明

package java.util.concurrent.locks;import java.util.concurrent.TimeUnit;/*** {@code Lock} implementations provide more extensive locking* operations than can be obtained using {@code synchronized} methods* and statements.  They allow more flexible structuring, may have* quite different properties, and may support multiple associated* {@link Condition} objects.* 额外的锁机制。* Lock具有更好的伸缩性,支持Condition条件对象。**

A lock is a tool for controlling access to a shared resource by


* multiple threads. Commonly, a lock provides exclusive access to a* shared resource: only one thread at a time can acquire the lock and* all access to the shared resource requires that the lock be* acquired first. However, some locks may allow concurrent access to* a shared resource, such as the read lock of a {@link ReadWriteLock}.* 通常限定每次一个线程访问共享变量。* 但是ReadWriteLock允许读锁并发访问共享资源。**


The use of {@code synchronized} methods or statements provides


* access to the implicit monitor lock associated with every object, but* forces all lock acquisition and release to occur in a block-structured way:* when multiple locks are acquired they must be released in the opposite* order, and all locks must be released in the same lexical scope in which* they were acquired.* synchronized释放锁的顺序必须是获取锁的相反顺序。**


While the scoping mechanism for {@code synchronized} methods


* and statements makes it much easier to program with monitor locks,* and helps avoid many common programming errors involving locks,* there are occasions where you need to work with locks in a more* flexible way. For example, some algorithms for traversing* concurrently accessed data structures require the use of* "hand-over-hand" or "chain locking": you* acquire the lock of node A, then node B, then release A and acquire* C, then release B and acquire D and so on. Implementations of the* {@code Lock} interface enable the use of such techniques by* allowing a lock to be acquired and released in different scopes,* and allowing multiple locks to be acquired and released in any* order.* 一般来说在使用synchronized来加锁会比较方便,减少出错的频率。* 但Lock显示锁的灵活性会很高。**


With this increased flexibility comes additional


* responsibility. The absence of block-structured locking removes the* automatic release of locks that occurs with {@code synchronized}* methods and statements. In most cases, the following idiom* should be used:**


{@code


* Lock l = ...;* l.lock();* try {*   // access the resource protected by this lock* } finally {*   l.unlock();* }}

* * When locking and unlocking occur in different scopes, care must be * taken to ensure that all code that is executed while the lock is * held is protected by try-finally or try-catch to ensure that the * lock is released when necessary. * Lock灵活性大,那么出错的几率高,使用Lock锁的时候要记得释放锁。 * *

{@code Lock} implementations provide additional functionality


* over the use of {@code synchronized} methods and statements by * providing a non-blocking attempt to acquire a lock ({@link * #tryLock()}), an attempt to acquire the lock that can be * interrupted ({@link #lockInterruptibly}, and an attempt to acquire * the lock that can timeout ({@link #tryLock(long, TimeUnit)}). * 获取锁是非阻塞的。能被中断。可以设置超时。 * *

A {@code Lock} class can also provide behavior and semantics


* that is quite different from that of the implicit monitor lock, * such as guaranteed ordering, non-reentrant usage, or deadlock * detection. If an implementation provides such specialized semantics * then the implementation must document those semantics. * 提高语义化,即知道哪里加锁了,哪里释放锁。 * *

Note that {@code Lock} instances are just normal objects and can


* themselves be used as the target in a {@code synchronized} statement. * Acquiring the * monitor lock of a {@code Lock} instance has no specified relationship * with invoking any of the {@link #lock} methods of that instance. * It is recommended that to avoid confusion you never use {@code Lock} * instances in this way, except within their own implementation. * 建议在使用的时候不要使用Lock实例作为内置锁,因为会导致混乱。 * 与真正的Lock锁混乱。 * *

Except where noted, passing a {@code null} value for any


* parameter will result in a {@link NullPointerException} being * thrown. * *

Memory Synchronization


* 实现内存可见性 * *

All {@code Lock} implementations must enforce the same


* memory synchronization semantics as provided by the built-in monitor * lock, as described in *

* The Java Language Specification (17.4 Memory Model): * * A successful {@code lock} operation has the same memory * synchronization effects as a successful Lock action.

* A successful {@code unlock} operation has the same * memory synchronization effects as a successful Unlock action.

* * * Unsuccessful locking and unlocking operations, and reentrant * locking/unlocking operations, do not require any memory * synchronization effects. * *

Implementation Considerations


* 根据具体的类来实现。 * *

The three forms of lock acquisition (interruptible,


* non-interruptible, and timed) may differ in their performance * characteristics, ordering guarantees, or other implementation * qualities. Further, the ability to interrupt the ongoing

* acquisition of a lock may not be available in a given {@code Lock} * class. Consequently, an implementation is not required to define * exactly the same guarantees or semantics for all three forms of * lock acquisition, nor is it required to support interruption of an * ongoing lock acquisition. An implementation is required to clearly * document the semantics and guarantees provided by each of the * locking methods. It must also obey the interruption semantics as * defined in this interface, to the extent that interruption of lock * acquisition is supported: which is either totally, or only on * method entry. * *

As interruption generally implies cancellation, and checks for


* interruption are often infrequent, an implementation can favor responding * to an interrupt over normal method return. This is true even if it can be * shown that the interrupt occurred after another action may have unblocked * the thread. An implementation should document this behavior. * * @see ReentrantLock * @see Condition * @see ReadWriteLock * * @since 1.5 * @author Doug Lea */ public interface Lock {

简单总结下就是:

1、Lock方式来获取锁支持中断、超时不获取、是非阻塞的。

2、提高了语义化,哪里加锁,哪里解锁都得写出来。

3、Lock显式锁可以给我们带来很好的灵活性,但同时我们必须手动释放锁。

4、支持Condition条件对象。

5、允许多个读线程同时访问共享资源。

synchronized锁和Lock锁使用哪个

Lock锁在刚出来的时候很多性能方面都比Synchronized锁要好,但是从JDK1.6开始Synchronized锁就做了各种的优化。

所以,到现在Lock锁和Synchronized锁的性能其实差别不是很大!而Synchronized锁用起来又特别简单。Lock锁还得顾忌到它的特性,要手动释放锁才行(如果忘了释放,这就是一个隐患)。

所以说,我们绝大部分时候还是会使用Synchronized锁,用到了Lock锁提及的特性,带来的灵活性才会考虑使用Lock显式锁。

公平锁:线程将按照它们发出请求的顺序来获取锁。

非公平锁:线程发出请求的时可以“插队”获取锁。

Lock和synchronize都是默认使用非公平锁的。如果不是必要的情况下,不要使用公平锁。

公平锁会来带一些性能的消耗的。