synchronized与Lock的区别
线程与进程
一个程序最少需要一个进程,而一个进程最少需要一个线程。关系是线程->进程->程序的大致组成结构。所以线程是程序执
行流的最小单位,而进程是系统进行资源分配和调度的一个独立单位。
Thread的几个重要方法
a、start()方法,调用该方法开始执行该线程;b、stop()方法,调用该方法强制结束该线程执行;c、join方法,调用该方法
等待该线程结束。d、sleep()方法,调用该方法该线程进入等待。e、run()方法,调用该方法直接执行线程的run()方法,但
是线程调用start()方法时也会运行run()方法,区别就是一个一个是由线程调度运行run()方法,一个是直接调用了线程中的
run方法!!
wait()与notify()方法是object的方法,不是Thread的方法!!同时,wait()与notify()会配合使用,分别表示线程挂起和
线程恢复。
这里还有一个很常见的问题:wait和sleep的区别,简单来说wait()会释放对象锁 而sleep()不会释放对象锁
1、线程自旋和适应性自旋
java线程其实是映射在内核之上的,线程的挂起和恢复会极大的影响开销。并且jdk官方人员发现,很多线程在等待锁的时候
,在很短的一段时间就获得了锁,所以他们在线程等待的时候,并不需要把线程挂起,而是让他无目的的循环,一般设置10
次。这样就避免了线程切换的开销,极大的提升了性能。
而适应性自旋,是赋予了自旋一种学习能力,它并不固定自旋10次一下。他可以根据它前面线程的自旋情况,从而调整它的
自旋,甚至是不经过自旋而直接挂起。
锁消除
就是把不必要的同步在编译阶段进行移除。
锁粗化
在用synchronized的时候,我们都讲究为了避免大开销,尽量同步代码块要小。那么为什么还要加粗呢?
轻量级锁
偏向锁
1、首先synchronized是java内置关键字,在jvm层面,Lock是个java类;
2、synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
3、synchronized会自动释放锁(a线程执行完同步代码会释放锁;b线程执行过程中发生异常会释放锁),Lock需在finally中手
工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
4、用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待
下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束;
5、synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)
6、Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。
lock():获取锁,如果锁被暂用则一直等待
unlock:释放锁
tryLock():注意返回类型是boolean,如果获取锁的时候锁被占用就返回false,否则返回true
tryLock(long time,TimeUnit unit):比起tryLock()就是给了一个时间期限,保证等待参数时间
lockInterruptibly():用该锁的获得方式,如果线程在获取锁的阶段进入了等待,那么可以中断此线程,先去做别的事
package com.trx.lockdemo;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
private Lock lock = new ReentrantLock();
/**
* 使用完毕释放后其他线程才能获取锁
*/
public void lockTest(Thread thread){
lock.lock();//获取锁
try {
System.out.println("线程"+thread.getName()+"获取当前锁");//打印当前锁的名称
Thread.sleep(2000);
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("线程"+thread.getName()+"发生了异常释放锁");
}finally{
System.out.println("线程"+thread.getName()+"执行完毕释放锁");
lock.unlock();
}
}
public static void main(String[] args){
final LockTest lockTest = new LockTest();
//声明一个线程 "线程一"
Thread thread1 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"thread1");
Thread thread2 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"Thread2");
Thread thread3 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"thread3");
Thread thread4 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"thread4");
Thread thread5 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"thread5");
Thread thread6 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"thread6");
thread6.start();
thread5.start();
thread4.start();
thread3.start();
thread2.start();
thread1.start();
}
}
执行结果:
线程thread6获取当前锁
线程thread6执行完毕释放锁
线程thread4获取当前锁
线程thread4执行完毕释放锁
线程thread5获取当前锁
线程thread5执行完毕释放锁
线程thread1获取当前锁
线程thread1执行完毕释放锁
线程Thread2获取当前锁
线程Thread2执行完毕释放锁
线程thread3获取当前锁
线程thread3执行完毕释放锁
public void tryLockTest(Thread thread){
if(lock.tryLock()){//尝试获取锁
try {
System.out.println("线程"+thread.getName()+"获取当前锁");//打印当前锁的名称
Thread.sleep(2000);
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("线程"+thread.getName()+"发生了异常释放锁");
}finally{
System.out.println("线程"+thread.getName()+"执行完毕释放锁");
lock.unlock();//释放锁
}
}else{
System.out.println("我是线程"+Thread.currentThread().getName()+"当前锁被别人占用,我
无法获取");
}
}
public static void main(String[] args){
final LockTest lockTest = new LockTest();
/* //声明一个线程 "线程一"
Thread thread1 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"thread1");
Thread thread2 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"Thread2");
Thread thread3 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"thread3");
Thread thread4 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"thread4");
Thread thread5 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"thread5");
Thread thread6 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"thread6");*/
//声明一个线程 "线程一"
Thread thread1 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.tryLockTest(Thread.currentThread());
}
},"thread1");
Thread thread2 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.tryLockTest(Thread.currentThread());
}
},"Thread2");
Thread thread3 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.tryLockTest(Thread.currentThread());
}
},"thread3");
Thread thread4 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.tryLockTest(Thread.currentThread());
}
},"thread4");
Thread thread5 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.tryLockTest(Thread.currentThread());
}
},"thread5");
Thread thread6 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.tryLockTest(Thread.currentThread());
}
},"thread6");
thread6.start();
thread5.start();
thread4.start();
thread3.start();
thread2.start();
thread1.start();
}
我是线程thread6当前锁被别人占用,我无法获取
我是线程thread3当前锁被别人占用,我无法获取
我是线程thread4当前锁被别人占用,我无法获取
线程thread5获取当前锁
我是线程thread1当前锁被别人占用,我无法获取
我是线程Thread2当前锁被别人占用,我无法获取
线程thread5执行完毕释放锁
public void tryLockParamTest(Thread thread) throws InterruptedException{
if(lock.tryLock(3000, TimeUnit.MILLISECONDS)){
try{
System.out.println("线程"+thread.getName()+"获取当前锁");//打印当前锁的名称
Thread.sleep(4000);//为看出执行效果,是线程此处休眠2秒
}catch(Exception e){
System.out.println("线程"+thread.getName()+"发生了异常释放锁");
}finally{
System.out.println("线程"+thread.getName()+"执行完毕释放锁");
lock.unlock();//释放锁
}
}else{
System.out.println("我是线程"+Thread.currentThread().getName()+"当前锁被别人占用,等
待3s后无法获取,放弃");
}
}
public static void main(String[] args){
final LockTest lockTest = new LockTest();
/* //声明一个线程 "线程一"
Thread thread1 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"thread1");
Thread thread2 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"Thread2");
Thread thread3 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"thread3");
Thread thread4 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"thread4");
Thread thread5 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"thread5");
Thread thread6 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.lockTest(Thread.currentThread());
}
},"thread6");*/
/*//声明一个线程 "线程一"
Thread thread1 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.tryLockTest(Thread.currentThread());
}
},"thread1");
Thread thread2 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.tryLockTest(Thread.currentThread());
}
},"Thread2");
Thread thread3 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.tryLockTest(Thread.currentThread());
}
},"thread3");
Thread thread4 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.tryLockTest(Thread.currentThread());
}
},"thread4");
Thread thread5 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.tryLockTest(Thread.currentThread());
}
},"thread5");
Thread thread6 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
lockTest.tryLockTest(Thread.currentThread());
}
},"thread6");*/
//声明一个线程 "线程一"
Thread thread1 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
try {
lockTest.tryLockParamTest(Thread.currentThread());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"thread1");
Thread thread2 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
try {
lockTest.tryLockParamTest(Thread.currentThread());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"Thread2");
Thread thread3 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
try {
lockTest.tryLockParamTest(Thread.currentThread());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"thread3");
Thread thread4 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
try {
lockTest.tryLockParamTest(Thread.currentThread());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"thread4");
Thread thread5 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
try {
lockTest.tryLockParamTest(Thread.currentThread());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"thread5");
Thread thread6 = new Thread(new Runnable(){
public void run() {
// TODO Auto-generated method stub
try {
lockTest.tryLockParamTest(Thread.currentThread());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
},"thread6");
thread6.start();
thread5.start();
thread4.start();
thread3.start();
thread2.start();
thread1.start();
}
线程thread5获取当前锁
我是线程Thread2当前锁被别人占用,等待3s后无法获取,放弃
我是线程thread1当前锁被别人占用,等待3s后无法获取,放弃
我是线程thread6当前锁被别人占用,等待3s后无法获取,放弃
我是线程thread3当前锁被别人占用,等待3s后无法获取,放弃
我是线程thread4当前锁被别人占用,等待3s后无法获取,放弃
线程thread5执行完毕释放锁
ReentrantReadWriteLock里面提供了很多丰富的方法,不过最主要的有两个方法:readLock()和writeLock()用来获取读锁和
写锁。假如有很多线程要同时进行读操作的话,先看一下synchronized达到的效果:
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Test {
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public static void main(String[] args){
final Test test = new Test();
new Thread(){
public void run(){
test.get(Thread.currentThread());
}
}.start();
new Thread(){
public void run(){
test.get(Thread.currentThread());
}
}.start();
}
public synchronized void get(Thread thread){
long start = System.currentTimeMillis();
while(System.currentTimeMillis()-start<=1){
System.out.println(thread.getName()+"正在进行读操作");
}
System.out.println(thread.getName()+"读操作完毕");
}
/*public void get(Thread thread){
rwl.readLock().lock();
try{
long start = System.currentTimeMillis();
while(System.currentTimeMillis()-start<=1){
System.out.println(thread.getName()+"正在进行读操作");
}
System.out.println(thread.getName()+"读操作完毕");
}finally{
rwl.readLock().unlock();
}
}*/
}
直到thread0执行完读操作之后,才会打印thread1执行读操作信息。
输出结果:
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-0读操作完毕
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1读操作完毕
package com.trx.lockdemo;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Test {
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public static void main(String[] args){
final Test test = new Test();
new Thread(){
public void run(){
test.get(Thread.currentThread());
}
}.start();
new Thread(){
public void run(){
test.get(Thread.currentThread());
}
}.start();
}
/* public synchronized void get(Thread thread){
long start = System.currentTimeMillis();
while(System.currentTimeMillis()-start<=1){
System.out.println(thread.getName()+"正在进行读操作");
}
System.out.println(thread.getName()+"读操作完毕");
}*/
public void get(Thread thread){
rwl.readLock().lock();
try{
long start = System.currentTimeMillis();
while(System.currentTimeMillis()-start<=1){
System.out.println(thread.getName()+"正在进行读操作");
}
System.out.println(thread.getName()+"读操作完毕");
}finally{
rwl.readLock().unlock();
}
}
}
thread0 和thread1同时进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-1正在进行读操作
Thread-0正在进行读操作
Thread-1读操作完毕
Thread-0读操作完毕
锁的相关概念介绍
1、可重入锁
如果锁具备可重入性,则称作为可重入锁。像synchronized和ReentrantLock都是可重入锁,可重入性在我看来实际上表明了
锁的分配机制:基于线程的分配,而不是基于方法调用的分配。举个简单的例子,当一个线程执行到某个synchronized方法
时,比如说method1,而在method1中会调用另外一个synchronized方法method2,此时线程不必重新去申请锁,而是可以直接
执行方法method2。
class MyClass {
public synchronized void method1() {
method2();
}
public synchronized void method2() {
}
}
上述代码中的两个方法method1和method2都用synchronized修饰了,假如某一时刻,线程A执行到了method1,此时线程A获取
了这个对象的锁,而由于method2也是synchronized方法,假如synchronized不具备可重入性,此时线程A需要重新申请锁。
但是这就会造成一个问题,因为线程A已经持有了该对象的锁,而又在申请获取该对象的锁,这样就会线程A一直等待永远不
会获取到的锁。
而由于synchronized和Lock都具备可重入性,所以不会发生上述现象。
可中断锁
可中断锁:顾名思义,就是可以相应中断的锁
在java中,synchronized就不是可中断锁,而Lock是可中断锁。
如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想处理其他
事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。
在前面演示lockInterruptibly()的用法时已经体现了Lock的可中断性。
3、公平锁
公平锁即尽量以请求锁的顺序来获取锁。比如同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最
先请求线程)会获得该锁,就是公平锁。
非公平锁即无法保证锁的获取是按照请求锁的顺序进行的。这样就可能导致某个或者一些线程永远获取不到锁。
在Java中,synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序。
而对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。
在ReentrantLock中定义了2个静态内部类,一个是NotFairSync,一个是FairSync,分别用来实现非公平锁和公平锁。
我们可以在创建ReentrantLock对象时,通过以下方式来设置锁的公平性:
另外在ReentrantLock类中定义了很多方法,比如:
isFair() //判断锁是否是公平锁
isLocked() //判断锁是否被任何线程获取了
isHeldByCurrentThread() //判断锁是否被当前线程获取了
hasQueuedThreads() //判断是否有线程在等待该锁
在ReentrantReadWriteLock中也有类似的方法,同样也可以设置为公平锁和非公平锁。不过要记住,
ReentrantReadWriteLock
并未实现Lock接口,它实现的是ReadWriteLock接口。
4、读写锁
读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。
正因为有了读写锁,才使得多线程之间的读操作不会发生冲突
ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。
可以通过readLock()获取读锁,通过writeLock()获取写锁。