JUC 多线程操作,先康康美团的博客吧!https://tech.meituan.com/2018/11/15/java-lock.html
Java多线程操作某个未加锁的资源
使用synchronized
之后:
1 2 3 4 5 6 7 8 9 10 11 class Ticket { private int num = 150 ; Lock lock = new ReentrantLock(); public synchronized void sale () { if (num>0 ){ System.out.println(Thread.currentThread().getName()+"第" +(150 -(--num))+"张票,剩余" +num); } } }
Lock锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Ticket2 { private int num = 50 ; Lock lock = new ReentrantLock(); public synchronized void sale () { lock.lock(); try { if (num>0 ){ System.out.println(Thread.currentThread().getName()+"第" +(150 -(--num))+"张票,剩余" +num); } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
synchronized与lock两者的区别 1、synchronized是一个内置的Java关键字,Lock是一个Java类;
2、synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁;
3、synchronized会自动释放锁,lock必须要手动释放锁,如果不释放锁会造成死锁 ;
4、synchronized使用时需要等待资源释放锁,而lock就不一定会等待下去;
5、synchronized可重入锁,不可以中断,非公平;lock,可重入锁,可中断锁,可以设置为公平锁;
6、适用范围:synchronized适合锁少量的代码同步问题,lock适合大量的同步代码。
锁是什么?如何判断锁住了什么资源?
生产者和消费者 等待唤醒、通知
虚假唤醒
常规版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class Data { private int num = 0 ; public synchronized void increment () throws InterruptedException { while (num!=0 ){ this .wait(); } num++; System.out.println(Thread.currentThread().getName()+"->" +num); this .notifyAll(); } public synchronized void decrement () throws InterruptedException { while (num==0 ){ this .wait(); } num--; System.out.println(Thread.currentThread().getName()+"->" +num); this .notifyAll(); } } new Thread(new Runnable() { @Override public void run () { for (int i = 0 ; i < 10 ; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } } },"A" ).start(); new Thread(new Runnable() { @Override public void run () { for (int i = 0 ; i < 10 ; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } } },"B" ).start();
JUC版本的生产者和消费者
Lock
替换synchronized
方法和语句的使用, Condition
取代了对象监视器方法的使用。
一个Condition
实例本质上绑定到一个锁。 要获得特定Condition
实例需要使用newCondition()方法。
例如,假设我们有一个有限的缓冲区,它支持put
和take
方法。 如果在一个空的缓冲区尝试一个take
,则线程将阻塞直到一个项目可用; 如果put
试图在一个完整的缓冲区,那么线程将阻塞,直到空间变得可用。 我们希望在单独的等待集中等待put
线程和take
线程,以便我们可以在缓冲区中的项目或空间可用的时候使用仅通知单个线程的优化。 这可以使用两个Condition
实例来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class BoundedBuffer { final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100 ]; int putptr, takeptr, count; public void put (Object x) throws InterruptedException { lock.lock(); try { while (count == items.length) notFull.await(); items[putptr] = x; if (++putptr == items.length) putptr = 0 ; ++count; notEmpty.signal(); } finally { lock.unlock(); } } public Object take () throws InterruptedException { lock.lock(); try { while (count == 0 ) notEmpty.await(); Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0 ; --count; notFull.signal(); return x; } finally { lock.unlock(); } } }
( ArrayBlockingQueue
类提供此功能,因此没有理由实现此示例使用类。)
Condition
实现可以提供Object
监视器方法的行为和语义,例如有保证的通知顺序,或者在执行通知时不需要锁定。 如果一个实现提供了这样的专门的语义,那么实现必须记录这些语义。
需要注意的是Condition
实例只是普通的对象,其本身作为一个目标synchronized
语句,可以有自己的监视器wait
和notification
个方法调用。 获取Condition
实例的监视器锁或使用其监视方法与获取与该Condition相关联的Condition
或使用其waiting
和signalling
方法没有特定关系。 建议为避免混淆,您永远不会以这种方式使用Condition
实例,除了可能在自己的实现之内。
除非另有说明,传递任何参数的null
值将导致NullPointerException
被抛出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 class Data2 { private int num = 0 ; final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); public void increment () throws InterruptedException { try { lock.lock(); while (num!=0 ){ notFull.await(); } num++; System.out.println(Thread.currentThread().getName()+"->" +num); notFull.signalAll(); } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } public void decrement () throws InterruptedException { try { lock.lock(); while (num==0 ){ notFull.await(); } num--; System.out.println(Thread.currentThread().getName()+"->" +num); notFull.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
Condition的优势:精准的通知和唤醒线程,即可以让线程有序执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 class Data2 { private int num = 1 ; final Lock lock = new ReentrantLock(); final Condition conditionA = lock.newCondition(); final Condition conditionB = lock.newCondition(); final Condition conditionC = lock.newCondition(); public void A () throws InterruptedException { try { lock.lock(); while (num!=1 ){ conditionA.await(); } num = 2 ; System.out.println(Thread.currentThread().getName()+"->" +num); conditionB.signal(); } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } public void B () throws InterruptedException { try { lock.lock(); while (num!=2 ){ conditionB.await(); } num=3 ; System.out.println(Thread.currentThread().getName()+"->" +num); conditionC.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void C () throws InterruptedException { try { lock.lock(); while (num!=3 ){ conditionC.await(); } num=1 ; System.out.println(Thread.currentThread().getName()+"->" +num); conditionA.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
锁 1 2 3 4 5 6 7 8 9 public static synchronized void sendSMS () { try { TimeUnit.SECONDS.sleep(2 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("SMS" ); }
并发操作异常ConcurrentModificationException 1 2 3 4 public static synchronized void call () { System.out.println("Call" ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class ListTest { public static void main (String[] args) { CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); for (int i = 0 ; i < 10 ; i++) { new Thread(new Runnable() { @Override public void run () { list.add(UUID.randomUUID().toString().substring(0 ,7 )); System.out.println(list); } },String.valueOf(i)).start(); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class SetTest { public static void main (String[] args) { Set<String> set = new CopyOnWriteArraySet<>(); for (int i = 0 ; i < 20 ; i++) { new Thread(new Runnable() { @Override public void run () { set.add(UUID.randomUUID().toString().substring(0 ,7 )); System.out.println(set); } },"A" ).start(); } } }