【JUC 一】线程 进程 synchronized Lock锁 生产者 消费者 8锁 线程安全集合类...

【JUC 一】线程 进程 synchronized Lock锁 生产者 消费者 8锁 线程安全集合类... 1、什么是JUCjava.util.concurrentjava.util.concurrent.atomicjava.util.concurrent.locksJUC就是三个并发工具包的简称业务: 普通的线程代码, 之前都是用的thread或者runnable接口但是相比于callable来说,thread没有返回值,且效率没有callable高2、线程和进程进程一个运行中的程序的集合一个进程往往可以包含多个线程至少包含一个线程java默认有几个线程?两个。 main线程、gc线程线程线程(thread)是操作系统能够进行运算调度的最小单位对于java而言如何创建thread继承自thread实现runnable接口实现callable接口Java真的可以开启线程吗?开不了的。底层是用native关键词修饰。调用本地实现(start0())public synchronized void start() { /** * This method is not invoked for the main method thread or system * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state NEW. */ if (threadStatus ! 0) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started * so that it can be added to the groups list of threads * and the groups unstarted count can be decremented. */ group.add(this); boolean started false; try { start0(); started true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } } //本地方法,调用底层c, java无法操作硬件 private native void start0();并发,并行并发编程: 并发和并行并发(多线程操作同一个资源,交替执行)CPU一核, 模拟出来多条线程,天下武功,唯快不破,快速交替并行(多个人一起行走, 同时进行)CPU多核,多个线程同时进行 ; 使用线程池操作public static void main(String[] args) { //获取CPU核数 //CPU 密集型,IO密集型 System.out.println(Runtime.getRuntime().availableProcessors()); }并发编程的本质:充分利用CPU的资源线程有几个状态?public enum State { // 新生 NEW, // 运行 RUNNABLE, // 阻塞 BLOCKED, // 等待 WAITING, //超时等待 TIMED_WAITING, //终止 TERMINATED; }区别waitsleep来自不同的类object类线程类锁的释放不同会释放锁不会释放锁使用范围不同必须在同步代码中可以在任何地方睡TimeUnitTimeUnit.SECONDS.sleep(3);//睡眠3秒3、Lock锁(重点)3.1、传统synchronized本质: 队列和锁放在方法上锁的是this放在代码块中锁的是()里面的对象synchronized(obj){ }3.2、Lock 接口reentrantLock构造器非公平锁NonfairSync()十分不公平可以插队(默认)公平锁FairSync()十分公平先来后到一定要排队public ReentrantLock() { sync new NonfairSync(); //无参默认非公平锁 } public ReentrantLock(boolean fair) { sync fair ? new FairSync() : new NonfairSync();//传参为true为公平锁 }3.3、synchronized 和 lock 区别比较Locksynchronized类型java接口内置关键字开启释放显式锁手动 开启 和 关闭隐式锁出了作用域自动释放代码锁支持支持方法锁不支持支持锁状态可以判断是否获取到了锁无法判断获取锁的状态线程阻塞Lock锁就不一定会等待下去线程1(获得锁阻塞)线程2(等待)可重入性可重入的可以判断锁可重入锁不可以中断的公平性默认非公平的(可设置)非公平的性能性能更好JVM将花费较少的时间来调度性能一般JVM将花费较多的时间来调度管理扩展性有更好的扩展性(提供更多的子类)不支持适用场景适合锁大量的同步代码适合锁少量的代码同步问题4、生产者和消费者问题面试高频单例模式、八大排序、生产者消费者、死锁4.1、synchronized实现wait、notify 必须在 synchronizied 声明的代码中否则会抛出异常 java.lang.IllegalMonitorStateExceptionpackage providerConsumer; /** * author ajun * Date 2021/7/3 * version 1.0 * synchronized版生产者消费者 */ public class Syn { public static void main(String[] args) { Data data new Data(); //线程1 new Thread(() - { for (int i 0; i 5; i) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },A).start(); //线程2 new Thread(() - { for (int i 0; i 5; i) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },B).start(); //线程3 new Thread(() - { for (int i 0; i 5; i) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },C).start(); //线程4 new Thread(() - { for (int i 0; i 5; i) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },D).start(); } } class Data{ private int num; public int getNum() { return num; } //增加 public synchronized void increment() throws InterruptedException { //判断等待 //用while不用if防止虚假唤醒 while(num!0){ this.wait(); } //业务代码 num; System.out.println(Thread.currentThread().getName() -- num); //通知 this.notifyAll(); } //减少 public synchronized void decrement() throws InterruptedException { //判断等待 //用while不用if防止虚假唤醒 while(num 0){ this.wait(); } //业务代码 num--; System.out.println(Thread.currentThread().getName() -- num); //通知 this.notifyAll(); } }可能存在的问题虚假唤醒在判断等待时如果用 if ,当线程多的时候可能会有虚假唤醒解决办法if 判断改为 while 判断因为 if 只会执行一次执行完会接着向下执行 if()外边的而 while 不会直到条件满足才会向下执行 while()外边的4.2、JUC实现在JUC中Lock 替换 synchronizedawait 替换 waitsignal 替换 notify。(signal 信号)package providerConsumer; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * author ajun * Date 2021/7/3 * version 1.0 */ public class Loc { public static void main(String[] args) { Data2 data2 new Data2(); new Thread(() - { for (int i 0; i 5; i) { try { data2.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },A).start(); new Thread(() - { for (int i 0; i 5; i) { try { data2.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },B).start(); new Thread(() - { for (int i 0; i 5; i) { try { data2.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },C).start(); new Thread(() - { for (int i 0; i 5; i) { try { data2.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },D).start(); } } class Data2{ private int num 0; Lock lock new ReentrantLock();//定义锁 Condition condition lock.newCondition();//定义同步监视器 public int getNum() { return num; } //增加 public void increment() throws InterruptedException { lock.lock();//加锁 try { //判断等待 while (num ! 0){ condition.await();//等待 } //业务代码 num; System.out.println(Thread.currentThread().getName() -- num); //通知 condition.signalAll(); } finally { lock.unlock();//解锁 } } //减少 public void decrement() throws InterruptedException { lock.lock();//加锁 try { //判断等待 while (num 0){ condition.await();//等待 } //业务代码 num--; System.out.println(Thread.currentThread().getName() -- num); //通知 condition.signalAll(); } finally { lock.unlock();//解锁 } } }Condition实现精准通知唤醒5、8锁现象8锁就是关于锁的八个现象① 非静态同步方法的默认锁是 this静态同步方法的默认锁是 class② 某一时刻内只能有一个线程有锁无论几个方法前提是用的同一把锁参考https://www.cnblogs.com/shamao/p/11045282.html5.1、2线程 1对象 2方法两个线程 调用 同一个对象 的 两个同步方法package lock8; /** * author ajun * Date 2021/7/4 * version 1.0 * 两个线程调用同一个对象的两个同步方法 */ public class Lock1 { public static void main(String[] args) { //同一对象 Number number new Number(); //线程1 new Thread(() - {number.getOne();},A).start(); //线程2 new Thread(() - {number.getTwo();},B).start(); } } class Number{ //同步方法1 (非static) public synchronized void getOne(){ System.out.println(Thread.currentThread().getName() one); } //同步方法2 (非static) public synchronized void getTwo(){ System.out.println(Thread.currentThread().getName() two); } }运行结果如下A one B two分析被 synchronized 修饰的方法锁的对象是方法的所有者。因为两个方法的所有者是同一个所以两个方法用的是同一个锁先调用方法的先获得锁先执行5.2、2线程 1对象 2方法(1sleep)新增sleep()给某个方法TimeUnit.SECONDS.sleep(2);分析不管在何处添加休眠后会中途休眠但不影响执行顺序。被synchronized修饰的方法锁的对象是方法的所有者。因为两个方法的所有者是同一个所以两个方法用的是同一个锁先调用方法的先获得锁先执行第二个方法只有在第一个方法执行完释放锁之后才执行5.3、3线程 1对象 3方法(1普通)新增一个线程调用 同一对象 新增的一个普通方法分析新增的方法没有被 synchronized 修饰不是同步方法不受锁的影响所以不需要等待。其他线程共用了一把锁所以还需要等待。5.4、2线程 2对象 2方法(1sleep)两个线程 调用 两个对象 的同步方法其中一个方法有sleep()分析被 synchronized 修饰的方法锁的对象是方法的调用者。因为用了两个对象调用各自的方法所以两个方法的调用者不是同一个所以两个方法用的不是同一个锁后调用的方法不需要等待先调用的方法5.5、2线程 1对象 2方法(1static)sleep()的方法设置为static并且让两个线程用同一个对象调用两个方法分析被synchronized和static修饰的方法锁的对象是类的class对象。仅仅被synchronized修饰的方法锁的对象是方法的调用者。因为两个方法锁的对象不是同一个所以两个方法用的不是同一个锁后调用的方法不需要等待先调用的方法。5.6、2线程 1对象 2方法(2static)将两个方法均设置为static方法并且让两个线程用同一个对象调用两个方法分析被synchronized和static修饰的方法锁的对象是类的class对象。因为两个同步方法都被static修饰了所以两个方法用的是同一个锁后调用的方法需要等待先调用的方法5.7、2线程 2对象 2方法(1static)将两个方法中有sleep()的方法设置为static方法另一个方法去掉static修饰让两个线程用 两个对象 调用两个方法分析被synchronized和static修饰的方法锁的对象是类的class对象。仅仅被synchronized修饰的方法锁的对象是方法的调用者。即便是用同一个对象调用两个方法锁的对象也不是同一个所以两个方法用的不是同一个锁后调用的方法不需要等待先调用的方法。5.8、2线程 2对象 2方法(2static)将两个方法均设置为static方法并且让两个线程用 两个对象 调用两个方法分析被synchronized和static修饰的方法锁的对象是类的class对象。因为两个同步方法都被static修饰了即便用了两个不同的对象调用方法两个方法用的还是同一个锁后调用的方法需要等待先调用的方法。5.9、总结普通同步方法一个类里面如果有多个synchronized方法在使用同一个对象调用的前提下某一个时刻内只要一个线程去调用其中的一个synchronized方法了其他的线程都只能等待换句话说某一时刻内只能有唯一一个线程去访问这些synchronized方法。锁的是当前对象this被锁定后其他线程都不能进入到当前对象的其他的synchronized方法。普通方法加个普通方法后发现和同步锁无关静态同步方法换成静态同步方法后情况又变化所有的非静态同步方法用的都是同一把锁实例对象本身也就是说如果一个对象的非静态同步方法获取锁后该对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁可是其他对象的非静态同步方法因为跟该对象的非静态同步方法用的是不同的锁所以毋须等待该对象的非静态同步方法释放锁就可以获取他们自己的锁所有的静态同步方法用的也是同一把锁类对象本身这两把锁是两个不同的对象所以静态同步方法与非静态同步方法之间不会有竞争条件。但是一旦一个静态同步方法获取锁后其他的静态同步方法都必须等待该方法释放锁后才能获取锁而不管是同一个对象的静态同步方法还是其他对象的静态同步方法只要它们属于同一个类的对象那么就需要等待当前正在执行的静态同步方法释放锁方法锁的类型等待情况普通方法没有锁不需要等待静态方法没有锁不需要等待普通同步方法对象锁同一对象内的多个普通同步方法间需要等待 不同对象调用时不需要等待静态同步方法类锁该类的任何对象调用都需要等待都是同一把类锁只需要判断是否是同一把锁6、线程安全集合类1传统Vector效率低2Collections.synchronizedXxx()效率低3JUC安全集合类效率高1CopyOnWriteArrayList并发下 ArrayList 是不安全的解决方案1、用线程安全的 Vector 替换 ArrayList底层是用 synchronized 效率不高2、用集合工具类把 ArrayList 转换为线程安全的底层是用 synchronized 效率不高Collections.synchronizedList(new ArrayList())3、使用 CopyOnWriteArrayList 替换 ArrayList写入时复制COW 是计算机程序设计领域的一种优化策略底层是用 Lock 锁效率比前两种方式高。读写分离。package jUCCollection; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; /** * author ajun * Date 2021/7/4 * version 1.0 */ public class CopyOnWriteArrayListTest { public static void main(String[] args) { //ListString list new ArrayList(); //ListString list new Vector(); //ListString list Collections.synchronizedList(new ArrayList()); ListString list new CopyOnWriteArrayList(); for (int i 0; i 20; i) { new Thread(() - { list.add(UUID.randomUUID().toString().substring(0,5)); System.out.println(Thread.currentThread().getName() : list); },String.valueOf(i)).start(); } } }2CopyOnWriteArraySet并发下 HashSet 是不安全的解决方案1、用集合工具类把 HashSet 转换为线程安全的底层是用 synchronized 效率不高Collections.synchronizedSet(new HashSet())2、使用 CopyOnWriteArraySet 替换 HashSet写入时复制COW 计算机程序设计领域的一种优化策略写入时调用 CopyOnWriteArrayList 的方法和 CopyOnWriteArrayList 基本一致略有区别底层是用 Lock 锁效率比前一种方式高。读写分离。3ConcurrentHashMap并发下 HashMap 是不安全的解决方案1、用集合工具类把 HashMap 转换为线程安全的效率不高Collections.synchronizedMap(new HashMap())2、使用 ConcurrentHashMap 替换 HashMap效率比前一种方式高