多线程访问一个共享资源时,可能会致使运行结果不是预计的结果。数据库
好比两个线程要往数据库中插入一条数据。安全
两个线程先检查数据库中有没有这个数据,都检测到没有,因而都插入一条数据。但实际上只须要一条数据。多线程
对共享资源操做时,才会有线程安全问题。线程
序列化访问临界资源。同一时刻,只有一个线程访问临界资源。也叫同步互斥访问。code
synchronized和Lock对象
做用是给共享资源加上互斥锁。当一个线程访问时,其余线程等待访问。接口
synchronized能够锁方法,也能够锁代码块。资源
synchronized的本质是锁对象。同步
Java中,每个对象都有一个所标记monitor。多线程访问某个对象时,线程只有得到了该对象的锁,才能访问。源码
1.一个线程访问一个对象的synchronized方法时,另外一个线程不能访问该对象的synchronized方法,可是能够访问非synchronized方法。
2.一个线程访问一个对象object1的synchronized方法时,另外一个线程能够访问对象object2的synchronized方法。object1和object2是同一个类型。 由于访问的对象不同。
3.一个线程访问一个对象的非静态synchronized方法时,另外一个线程能够访问该对象的静态synchronized方法。
由于静态synchronized方法,占领的锁是类锁,不是对象锁。
synchronized编译成两条指令:monitorenter和monitorexit
monitorenter会让对象的锁计数加1。 monitorexist会让对象的锁计数减1。
抛出异常时,会释放锁对象,不会出现死锁。
synchronized释放锁有两种状况:1.线程执行完,2.出现中断异常。
若是synchronized中线程阻塞,其余线程就要一直等待,很是影响效率。
因而就提供了功能更强大的Lock,来解决一些问题
public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); }
synchronized是Java的内置关键字。Lock是一个接口。
Lock中有这么几个方法:
lock()用来获取锁,若是其余线程已经获取了锁,那么就等待。
和synchronized不同。Lock不会自动释放锁。因此必须手动释放锁,用unLock()。 通常放在try...catch()...finally()中。
tryLock有返回值,尝试获取锁,若是获取了锁,就返回true,没有获取锁,就返回false。也就是说,这个方法不会等待锁。
通常用if(tryLock())..else...
使用lockInterruptibly()获取锁的状况下,若是线程处于等待获取锁的状态,调用interrupt能够中断线程,抛出异常。
实现了Lock的类。能够实现可重入锁。
是一个接口。定义了两个方法。ReentrantReadWriteLock实现了该接口。
readLock()和writeLock()分别获取读锁和写锁。
一个线程获取了读锁,另外一个线程能够得到该对象的读锁,不能得到该对象的写锁。
一个线程获取了写锁,另外一个线程不能够获取该对象的读写锁。
支持降级,写锁能够降级为读锁,可是读锁不能升级为写锁。
1.synchronized是Java内置关键字,Lock是接口。
2.synchronized异常会自动释放锁,Lock要手动释放。
3.synchronized没法中断等待获取锁的线程,Lock能够。
4.synchronized没法判断线程是否获取锁,Lock能够。
5.Lock可使用读锁,提升读线程的效率。
一个锁方法中调用另外一个锁方法,线程不须要从新获取锁对象。
synchronized和Lock都是可重入锁
等待获取锁的线程按请求顺序获取锁对象。
synchronized是非公平锁。 Lock默认是非公平锁。可是能够设置为公平锁。
ReentrantLock lock = new ReentrantLock(true);
能够中断等待锁的线程。
synchronized是不可中断锁。Lock是可中断锁。
锁分红两个,一个读锁,一个写锁。
synchronized没有,ReadWriteLock是读写锁。