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();
}
}
}