一、synchronized
是jvm的一个关键字,使用过程均由jvm控制
有三种使用方式:
- 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
- 修饰代码块,同方法
- 修饰静态方法,作用于当前类加锁,进入同步代码前要获得当前类对象的锁
1.1、实例方法
作用于实例,会阻塞其他线程访问本实例,但不会影响其他线程访问其他实例
如果实例中有多个synchronized修饰的方法,当其中一个方法被访问时,其他方法也不可以被访问,所以我们在用synchronized关键字的时候,能缩小代码段的范围就尽量缩小,能在代码段上加同步就不要再整个方法上加同步
【示例】
import java.util.concurrent.TimeUnit; /** * Created by jyy on 2018/6/21. */ public class SyncTest { public synchronized void test1(){ System.out.println("test1"); try { TimeUnit.SECONDS.sleep(10); }catch(InterruptedException ie){ System.out.println("中断异常"); } System.out.println("test1"); } public synchronized void test2(){ System.out.println("test2"); } public void test3(){ System.out.println("test3"); } }
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Created by jyy on 2018/4/19. */ public class Test { public static void main(String[] args){ //需要声明为final类型 final SyncTest syncTest = new SyncTest(); //线程池 ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(new Runnable() { @Override public void run() { syncTest.test1(); } }); executorService.execute(new Runnable() { @Override public void run() { syncTest.test2(); } }); executorService.execute(new Runnable() { @Override public void run() { syncTest.test3(); } }); } }
执行结果:
test1
test3
test1
test2
1.2、代码块
作用于实例,同上
【示例】
//新增三个方法 public void test4(){ System.out.println("test4"); synchronized(this){ try { TimeUnit.SECONDS.sleep(10); }catch(InterruptedException ie){ System.out.println("中断异常"); } System.out.println("test4"); } } public void test5(){ System.out.println("test5"); synchronized(this){ System.out.println("test5"); } } public void test6(){ System.out.println("test6"); }
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Created by jyy on 2018/4/19. */ public class Test { public static void main(String[] args){ //需要声明为final类型 final SyncTest syncTest = new SyncTest(); //线程池 ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(new Runnable() { @Override public void run() { syncTest.test4(); } }); executorService.execute(new Runnable() { @Override public void run() { syncTest.test5(); } }); executorService.execute(new Runnable() { @Override public void run() { syncTest.test6(); } }); } }
执行结果:
test4
test5
test6
test4
test5
从执行结果上,可以看出,当访问关键字synchronized修饰的代码块时,其他synchronized修饰的代码块也会被锁住
1.3、静态方法
作用于类,会阻塞其他线程访问本类的同步方法,即使是不同的实例也不行
【示例】
//新增方法 public synchronized static void test7(){ int random= (new Random()).nextInt(100); System.out.println(random); try { TimeUnit.SECONDS.sleep(10); }catch(InterruptedException ie){ System.out.println("中断异常"); } System.out.println(random); }
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Created by jyy on 2018/4/19. */ public class Test { public static void main(String[] args){ //线程池 ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(new Runnable() { @Override public void run() { SyncTest.test7(); } }); executorService.execute(new Runnable() { @Override public void run() { SyncTest.test7(); } }); } }
执行结果:
69 69 67 67
二、lock
Lock是一个接口,其中lock()、lockInterruptibly()、tryLock()、tryLock(long time, TimeUnit unit)方法可以获取锁,unlock()用来释放锁
如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。
2.1、ReentrantLock
ReentrantLock是实现了Lock接口的类,通过它,我们来看下四种获取锁方法的区别
2.1.1、lock()
【示例1】
import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Created by jyy on 2018/6/25. */ public class ReentrantLockTest { private Lock lock = new ReentrantLock();//需要声明为全局变量 public void test1(){ lock.lock();//获取锁 try { System.out.println(Thread.currentThread()+"得到了锁"); TimeUnit.SECONDS.sleep(5); } catch (Exception e) { System.out.println("发生异常"); }finally { System.out.println(Thread.currentThread()+"释放了锁"); lock.unlock(); } } }
//线程池 ExecutorService executorService = Executors.newCachedThreadPool(); final ReentrantLockTest reentrantLockTest = new ReentrantLockTest(); executorService.execute(new Runnable() { @Override public void run() { reentrantLockTest.test1(); } }); executorService.execute(new Runnable() { @Override public void run() { reentrantLockTest.test1(); } });
执行结果:
Thread[pool-1-thread-1,5,main]得到了锁 Thread[pool-1-thread-1,5,main]释放了锁 Thread[pool-1-thread-2,5,main]得到了锁 Thread[pool-1-thread-2,5,main]释放了锁
【示例2】
//线程池 ExecutorService executorService = Executors.newCachedThreadPool(); final ReentrantLockTest reentrantLockTest = new ReentrantLockTest(); executorService.execute(new Runnable() { @Override public void run() { reentrantLockTest.test1(); } }); final ReentrantLockTest reentrantLockTest1 = new ReentrantLockTest(); executorService.execute(new Runnable() { @Override public void run() { reentrantLockTest1.test1(); } });
执行结果:
Thread[pool-1-thread-1,5,main]得到了锁 Thread[pool-1-thread-2,5,main]得到了锁 Thread[pool-1-thread-1,5,main]释放了锁 Thread[pool-1-thread-2,5,main]释放了锁
示例2说明Lock锁住的是实例,而不是类
2.1.2、tryLock()
tryLock()尝试获取锁,获取不到返回false,不影响代码继续执行
【示例】
//新增方法 public void test2(){ if(lock.tryLock())//获取锁 { try { System.out.println(Thread.currentThread() + "得到了锁"); TimeUnit.SECONDS.sleep(5); } catch (Exception e) { System.out.println("发生异常"); } finally { System.out.println(Thread.currentThread() + "释放了锁"); lock.unlock(); } }else{ System.out.println(Thread.currentThread() + "获取锁失败"); } }
//线程池 ExecutorService executorService = Executors.newCachedThreadPool(); final ReentrantLockTest reentrantLockTest = new ReentrantLockTest(); executorService.execute(new Runnable() { @Override public void run() { reentrantLockTest.test2(); } }); executorService.execute(new Runnable() { @Override public void run() { reentrantLockTest.test2(); } });
执行结果:
Thread[pool-1-thread-1,5,main]得到了锁 Thread[pool-1-thread-2,5,main]获取锁失败 Thread[pool-1-thread-1,5,main]释放了锁
2.1.3、tryLock(long time, TimeUnit unit)
【示例】
//新增方法 public void test3() throws InterruptedException { if(lock.tryLock(10,TimeUnit.SECONDS))//获取锁 { try { System.out.println(Thread.currentThread() + "得到了锁"); TimeUnit.SECONDS.sleep(5); } catch (Exception e) { System.out.println("发生异常"); } finally { System.out.println(Thread.currentThread() + "释放了锁"); lock.unlock(); } }else{ System.out.println(Thread.currentThread() + "获取锁失败"); } }
//线程池 ExecutorService executorService = Executors.newCachedThreadPool(); final ReentrantLockTest reentrantLockTest = new ReentrantLockTest(); executorService.execute(new Runnable() { @Override public void run() { try { reentrantLockTest.test3(); }catch(InterruptedException ie){ System.out.println("发生异常"); } } }); executorService.execute(new Runnable() { @Override public void run() { try { reentrantLockTest.test3(); }catch(InterruptedException ie){ System.out.println("发生异常"); } } });
执行结果:
Thread[pool-1-thread-1,5,main]得到了锁 Thread[pool-1-thread-1,5,main]释放了锁 Thread[pool-1-thread-2,5,main]得到了锁 Thread[pool-1-thread-2,5,main]释放了锁
尝试10s获取锁,获取不到才会返回false
2.1.4、lockInterruptibly()
【示例】
//新增方法 public void test4() throws InterruptedException { lock.lockInterruptibly(); try { System.out.println(Thread.currentThread()+"得到了锁"); TimeUnit.SECONDS.sleep(10); } catch (Exception e) { System.out.println("中断成功"); }finally { System.out.println(Thread.currentThread()+"释放了锁"); lock.unlock(); } }
final ReentrantLockTest reentrantLockTest = new ReentrantLockTest(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { try { reentrantLockTest.test4(); }catch(InterruptedException ie){ System.out.println("发生异常"); } } },"1"); thread1.start(); try { TimeUnit.SECONDS.sleep(3); } catch (Exception e) { System.out.println("延迟5s"); } System.out.println("开始中断"); thread1.interrupt();//中断thread1 Thread thread2 = new Thread(new Runnable() { @Override public void run() { try { reentrantLockTest.test4(); }catch(InterruptedException ie){ System.out.println("发生异常"); } } },"2"); thread2.start();
执行结果:
Thread[1,5,main]得到了锁 开始中断 中断成功 Thread[1,5,main]释放了锁 Thread[2,5,main]得到了锁 Thread[2,5,main]释放了锁
成功中断thread1的锁
三、读写锁
3.1、ReadWriteLock
ReadWriteLock是一个接口,只包含两个方法readLock()、writeLock()
3.2、ReentrantReadWriteLock
ReentrantReadWriteLock实现了ReadWriteLock接口,下面主要说一下ReadLock()、WriteLock()两个方法
【示例1】
import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Created by jyy on 2018/6/26. */ public class ReentrantReadWriteLockTest { private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); /** * 读锁 */ public void test1(){ rwl.readLock().lock(); try { System.out.println(Thread.currentThread() + "得到了读锁"); TimeUnit.SECONDS.sleep(5); } catch (Exception e) { System.out.println("发生异常"); } finally { System.out.println(Thread.currentThread() + "释放了读锁"); rwl.readLock().unlock(); } } /** * 写锁 */ public void test2(){ rwl.writeLock().lock(); try { System.out.println(Thread.currentThread() + "得到了写锁"); TimeUnit.SECONDS.sleep(5); } catch (Exception e) { System.out.println("发生异常"); } finally { System.out.println(Thread.currentThread() + "释放了写锁"); rwl.writeLock().unlock(); } } }
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Created by jyy on 2018/4/19. */ public class Test { public static void main(String[] args) throws InterruptedException { final ReentrantReadWriteLockTest reentrantReadWriteLockTest = new ReentrantReadWriteLockTest(); ExecutorService executorService = Executors.newCachedThreadPool();//连接池 executorService.execute(new Runnable() { @Override public void run() { reentrantReadWriteLockTest.test1(); } }); executorService.execute(new Runnable() { @Override public void run() { reentrantReadWriteLockTest.test1(); } }); executorService.execute(new Runnable() { @Override public void run() { reentrantReadWriteLockTest.test1(); } }); } }
执行结果:
Thread[pool-1-thread-1,5,main]得到了读锁 Thread[pool-1-thread-2,5,main]得到了读锁 Thread[pool-1-thread-3,5,main]得到了读锁 Thread[pool-1-thread-3,5,main]释放了读锁 Thread[pool-1-thread-1,5,main]释放了读锁 Thread[pool-1-thread-2,5,main]释放了读锁
多个线程都可以获取到读锁
【示例2】
executorService.execute(new Runnable() { @Override public void run() { reentrantReadWriteLockTest.test1(); } }); executorService.execute(new Runnable() { @Override public void run() { reentrantReadWriteLockTest.test1(); } }); executorService.execute(new Runnable() { @Override public void run() { reentrantReadWriteLockTest.test2(); } });
执行结果:
Thread[pool-1-thread-1,5,main]得到了读锁 Thread[pool-1-thread-2,5,main]得到了读锁 Thread[pool-1-thread-2,5,main]释放了读锁 Thread[pool-1-thread-1,5,main]释放了读锁 Thread[pool-1-thread-3,5,main]得到了写锁 Thread[pool-1-thread-3,5,main]释放了写锁
访问写锁时,必须等读锁全部释放
【示例3】
executorService.execute(new Runnable() { @Override public void run() { reentrantReadWriteLockTest.test2(); } }); executorService.execute(new Runnable() { @Override public void run() { reentrantReadWriteLockTest.test1(); } }); executorService.execute(new Runnable() { @Override public void run() { reentrantReadWriteLockTest.test2(); } });
执行结果:
Thread[pool-1-thread-1,5,main]得到了写锁 Thread[pool-1-thread-1,5,main]释放了写锁 Thread[pool-1-thread-2,5,main]得到了读锁 Thread[pool-1-thread-2,5,main]释放了读锁 Thread[pool-1-thread-3,5,main]得到了写锁 Thread[pool-1-thread-3,5,main]释放了写锁
想要获取读锁,必须先等到写锁释放
总结:
1、如果一个线程占用了读锁,其他线程依然可以获取到这个读锁,但不可以获取到写锁
2、如果一个线程占用了写锁,其他线程不可以获取到读锁和写锁
四、synchronized与lock的区别
1)synchronized是java的一个关键字,而Lock是一个接口
2)synchronized发生异常,会自动释放占有的锁,而Lock必须要主动释放锁,否则会一直处于占用状态
3)Lock中的lockInterruptibly()可以响应中断,而synchronized不可以
4)Lock中的tryLock()可以尝试获取锁,判断是否成功获取到锁,而synchronized不可以
5)ReentrantReadWriteLock可以声明读写锁,提高查询效率
两者不是非一即二的存在,具体使用要看不同的场景
五、锁类型
可重入锁:在执行对象中所有同步方法不用再次获得锁
举个简单的例子,当一个线程执行到某个synchronized方法时,比如说method1,而在method1中会调用另外一个synchronized方法method2,此时线程不必重新去申请锁,而是可以直接执行方法method2
synchronized和ReentrantLock都是可重入锁
可中断锁:在等待获取锁过程中可中断,如ReentrantLock中的lockInterruptibly()
公平锁: 按等待获取锁的线程的等待时间进行获取,等待时间长的具有优先获取锁权利
ReentrantLock默认是非公平锁,可以通过new ReentrantLock(true)声明为公平锁
读写锁:对资源读取和写入的时候拆分为两部分处理,读的时候可以多线程一起读,写的时候必须同步地写,如ReentrantReadWriteLock