进程:系统分配资源的单位;
线程:处理器任务调度和执行的单位,线程之间共享进程资源。
学习大纲:
在上一章我们已经学习了保证线程安全的前两种方式,今天来学习一下同步锁对象Lock锁吧!
线程同步——同步代码块+同步方法
小红在成长,公众号:小红的成长日记Java开发——38.多线程_(线程安全)
Lock锁:
Lock锁也是一个接口,有别于实现Runnable接口,它相当于同步监视器(锁),它是一个对象。
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
使用Lock锁的步骤:
//1.创建多线程(继承Thread类/实现Runnable接口)//2.重写/实现run()//3.在run()外,创建Lock接口的实例化对象//4.在存在安全隐患的地方,设置锁myLock.lock()//5.在finally()中解锁声明格式private (static) ReentrantLockmyLock=newReentrantLock(); myLock.lock(); //这种写法针对没有异常需要处理的情况try{ //需要同步的代码}finally{ myLock.unlock(); }
使用Lock锁为什么要用try...finally中?
为了保证unlock()一定会执行!如果在try中发生异常无法处理会导致无法解锁,容易形成死锁,造成程序僵持等问题...
定义的lock()方法为什么要声明在try外?
如果声明在try内,如果发生myLock.lock()发生异常,此时并没有获取到锁,finally中的myLock.unlock(),就无法正常解锁(线程中断异常 InterruptedException)!
三个窗口卖100张票,并保证线程同步。
此处使用实现Runnable接口实现;继承Thread类需要保证多个线程共用一个ReentrantLock对象,声明其为static静态的。
publicclassDemo { publicstaticvoidmain(String[] args) { Ticketstickets=newTickets(); Threadthread01=newThread(tickets); Threadthread02=newThread(tickets); Threadthread03=newThread(tickets); thread01.setName("窗口01"); thread02.setName("窗口02"); thread03.setName("窗口03"); thread01.start(); thread02.start(); thread03.start(); } } //1.创建多线程类classTicketsimplementsRunnable{ privateintticket=100; //3.创建Lock接口的实现类对象,获取锁privateReentrantLockmyLock=newReentrantLock(); //2.实现run()方法publicvoidrun() { while (true) { //4.设置锁myLock.lock(); try { if (ticket>0) { try { Thread.sleep(100); } catch (InterruptedExceptione) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() +":买票,票号为:"+ticket); ticket--; } else { break; } } finally { //5.解锁myLock.unlock(); } } } }
synchronized和Lock锁的区别:
1. Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放 synchronized(同步代码块和同步方法):不需要手动解锁,在同步代码块/同步方法结束后会自动释放锁;Lock锁需要手动释放锁;
2. Lock只有代码块锁,synchronized有代码块锁和方法锁;
3. 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有 更好的扩展性(提供更多的子类)。
优先使用顺序:Lock锁 -> 同步代码块(已经进入了方法体,分配了相应资源)-> 同步方法 (在方法体之外)
死锁:
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
解释:(电视未开机,两个人想看不同频道的电视节目,只有一个遥控器;A需要遥控器,B也需要遥控器,A不让B,B不让A,然后两个人都无法看电视,相互僵持...)
线程的如果形成死锁是不会出现异常、提示和终止程序,只是所有线程都处于阻塞状态,无法继续执行。
解决办法:
1.专门的算法、原则;
2.尽量减少同步资源的定义;
3.尽量避免嵌套同步。
案例:
StringBuffer进来学习!
小红在成长,公众号:小红的成长日记Java开发——16.常用类(Scanner、Object、String、StringBuffer、StringBuilder)
publicclassDemo { publicstaticvoidmain(String[] args) { StringBufferstr01=newStringBuffer(); StringBufferstr02=newStringBuffer(); //1.继承Thread类实现开线程newThread(){ //super.run();publicvoidrun() { synchronized (str01) { str01.append("a"); str02.append("1"); try { Thread.sleep(100); } catch (InterruptedExceptione) { e.printStackTrace(); } synchronized (str02) { str01.append("b"); str02.append("2"); System.out.println(str01); System.out.println(str02); } } } }.start(); //2.实现Runnable接口实现开线程newThread(newRunnable() { publicvoidrun() { synchronized (str02) { str01.append("c"); str02.append("3"); try { Thread.sleep(100); } catch (InterruptedExceptione) { e.printStackTrace(); } synchronized (str01) { str01.append("d"); str02.append("4"); System.out.println(str01); System.out.println(str02); } } } }).start(); } }