1、synchronized锁

synchronized是一种互斥锁,synchronized是Java的一个关键字,它能够将代码块(方法)锁起来,只要在代码块(方法)添加关键字synchronized,即可以实现同步的功能,一次只能允许一个线程进入被锁住的代码块。

1.1、synchronized用处是什么?

synchronized保证了线程的原子性。(被保护的代码块是一次被执行的,没有任何线程会同时访问)

synchronized还保证了可见性。(当执行完synchronized之后,修改后的变量对其他的线程是可见的)

1.2、synchronized是一种同步锁,可以修改一下几种:

1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;

2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;

3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

出处:

代码例子:

 

2、Lock

可以支持多个相关的Condition对象,Condition接口将Object监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock实现组合使用,为每个对象提供多个等待set(wait-set)。其中,Lock替代了synchronized方法和语句的使用,Condition替代了 Object监视器方法的使用。

Lock是Java实现的,与底层的JVM无关。在java.util.concurrent.locks包中有很多Lock的实现类,常用的有ReentrantLock、ReadWriteLock(实现类ReentrantReadWriteLock),其实现都依赖java.util.concurrent.AbstractQueuedSynchronizer类(简称AQS。

2.1、由于ReentrantLock是java.util.concurrent包下提供的一套互斥锁,相比Synchronized,ReentrantLock类提供了一些高级功能,主要有以下3项:

        1.等待可中断,持有锁的线程长期不释放的时候,正在等待的线程可以选择放弃等待,这相当于Synchronized来说可以避免出现死锁的情况。

        2.公平锁,多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好。

        3.锁绑定多个条件,一个ReentrantLock对象可以同时绑定对个对象。

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

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

支持Condition条件对象

 

2.2、synchronied和ReentrantLock的区别

这两种方式最大区别就是对于Synchronized来说,它是java语言的关键字,是原生语法层面的互斥,需要jvm实现。Lock是一个接口,ReentrantLock它是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成。

 

1、ReentarntLock可以添加多个检控条件(condition),但是synchronized只可以添加一个;

2、ReentarntLock可以控制得到锁的顺序(公平锁),也可以和synchronized一样使用非公平锁;

3、ReentarntLock支持获取锁超时(tryLock()方法)以及获取锁响应中断的操作(lockInterruptibly()方法,synchronized不支持。

4、在高争用条件下,ReentarntLock的可伸缩性优于synchronized。

5、通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

6、Lock可以提高多个线程进行读操作的效率。

 

2.3、ReentrantLock的缺点

1、必须在finally块中手动释放锁;synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁。

 

总结:synchronized好用,简单,性能不差。没有使用到Lock显式锁的特性就不要使用Lock锁了。

package ex09;

import java.util.concurrent.locks.ReentrantLock;

public class SynchronizedTest
{
    public static void main(String[] args)
    {
        MyThread1 t1 = new MyThread1();
        new Thread(t1, "t11").start();
        new Thread(t1, "t12").start();

        MyThread2 t2 = new MyThread2();
        new Thread(t2, "t21").start();
        new Thread(t2, "t22").start();

    }

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

    public void test2()
    {
        // 修饰代码块,此时用的锁是对象(内置锁)--->this
        // 我们使用synchronized修饰代码块时未必使用this,还可以使用其他的对象(随便一个对象都有一个内置锁)
        synchronized (this)
        {
            // doSomething
        }
    }

}

class MyThread1 implements Runnable
{
    public void run()
    {
        synchronized (this)
        {
            for (int i = 0; i < 10; i++)
                System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}

class MyThread2 implements Runnable
{
    private ReentrantLock lock = new ReentrantLock();
    public void run()
    {
        lock.lock();
        try
        {
            for (int i = 0; i < 5; i++)
                System.out.println(Thread.currentThread().getName() + ":" + i);
        } finally
        {
            lock.unlock();
        }
    }
}