1:回顾多线程
进程和线程是什么
进程是操作系统分配资源的最小单元,而线程是cpu调度的最小单元。
- java默认有几个线程
2个,main线程和GC线程(GC垃圾回收机制)
- java可以开启线程么
不能
- 并发和并行
并发,多线程操作同一个资源,cpu单核,模拟多条线程,快速交替
并行,多人一起走,cpu多核,多个线程可以同时执行,线程池
package main; public class Demo1 { public static void main(String[] args) { //获取cpu的核数 //cpu密集型,io密集型 System.out.println(Runtime.getRuntime().availableProcessors()); } }
示例:
线程有几个状态:
Thread.State
public enum State { /** * 新建状态 */ NEW, /** * 运行状态 */ RUNNABLE, /** * 堵塞状态 */ BLOCKED, /** * 等待状态 */ WAITING, /** * 超时等待 */ TIMED_WAITING, /** * 终止状态 */ TERMINATED; }
1.1 wait/sleep 的区别
1.来自不同类,wait->Object,sleep->Thread
2.锁的释放,wait->释放锁,sleep->不释放锁
3.使用范围,wait->同步代码块,sleep->任何地方
1.2 synchronized锁
package main; /* * 真正的多线程开发,公司中的开发,降低耦合型 * 线程就是一个单独的资源类,没有任何附属的操作! * 1. 属性 方法 * */ public class TicketSale { public static void main(String[] args) { //并发:多线程操作同一个资源类,把资源丢入线程 Ticket ticket = new Ticket(); //@FunctionalInterface 函数式接口,jkd1.8 lambda 表达式(参数)->{代码} new Thread(()->{ for (int i = 0; i < 30; i++) { ticket.sale(); } },"A").start(); new Thread(()->{ for (int i = 0; i < 30; i++) { ticket.sale(); } },"B").start(); new Thread(()->{ for (int i = 0; i < 30; i++) { ticket.sale(); } },"C").start(); } } //资源类OOP class Ticket{ //属性 方法 private int number = 30; //卖票的方式 public synchronized void sale(){ if (number>0){ System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余"+number); } } }
1.3 Lock 锁
Class ReentrantLock 构造方法 | |
public ReentrantLock() | 创建一个ReentrantLock的实例。 这相当于使用ReentrantLock(false) |
public ReentrantLock(boolean fair) | 根据给定的公平政策创建一个 ReentrantLock的实例. fair - true如果此锁应使用合理的订购策略 |
1.3.1 什么是公平锁,非公平锁?
- 非公平锁:可以插队(无参构造方法默认为非公平锁)
- 公平锁:先来后到(有参构造方法传值true时为公平锁)
package main; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /* * 真正的多线程开发,公司中的开发,降低耦合型 * 线程就是一个单独的资源类,没有任何附属的操作! * 1. 属性 方法 * */ public class TicketSale2 { public static void main(String[] args) { //并发:多线程操作同一个资源类,把资源丢入线程 Ticket2 ticket = new Ticket2(); //@FunctionalInterface 函数式接口,jkd1.8 lambda 表达式(参数)->{代码} new Thread(()->{ for (int i = 0; i < 30; i++) { ticket.sale(); } },"A").start(); new Thread(()->{ for (int i = 0; i < 30; i++) { ticket.sale(); } },"B").start(); new Thread(()->{ for (int i = 0; i < 30; i++) { ticket.sale(); } },"C").start(); } } /* * lock三部曲 * 1.new ReentrantLock() * 2.lock.lock();//加锁 * 3.finally-> lock.unlock() //解锁 * */ class Ticket2{ //属性 方法 private int number = 30; //卖票的方式 Lock lock = new ReentrantLock(); public void sale(){ lock.lock();//加锁 try { //业务代码 if (number>0){ System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"票,剩余"+number); } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock();//解锁 } } } /* 公平锁 线程1购买了第10张票 线程2购买了第9张票 线程3购买了第8张票 线程1购买了第7张票 线程2购买了第6张票 线程3购买了第5张票 线程1购买了第4张票 线程2购买了第3张票 线程3购买了第2张票 线程1购买了第1张票 非公平锁 线程1购买了第10张票 线程1购买了第9张票 线程1购买了第8张票 线程1购买了第7张票 线程1购买了第6张票 线程1购买了第5张票 线程1购买了第4张票 线程1购买了第3张票 线程1购买了第2张票 线程1购买了第1张票 */
1.4 synchronized 和 Lock 区别
- lock是一个接口,而synchronized是java的一个关键字。
- synchronized在发生异常时会自动释放占有的锁,因此不会出现死锁;而lock发生异常时,不会主动释放占有的锁,必须手动来释放锁,可能引起死锁的发生。
- Synchronized 可重入锁,不可以中断的,非公平; Lock ,可重入锁,可以判断锁,非公平(可以自己设置);
- Synchronized适合锁少量的代码同步问题,Lock适合锁大量的同步代码!
- Synchronized 线程1(获得锁,阻塞)、绩程2(等待,傻傻的等) ; Lock锁就不一定会等待下去;
- Synchronized无法判断获取锁的状态,Lock 可以判断是否获取到了锁
synchronized
Lock
1.5 synchronized 版的生产者和消费者
package pc; /* * 线程之间的通信问题:生产者和消费者问题 等待唤醒 ,通知唤醒 * 线程交替执行 A B 操作同一个变量 num = 0; * A num+1 * B num-1 * */ public class A { public static void main(String[] args) { Data data = new Data(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); } } //等待 业务 通知 class Data{//数字 资源类 private int number = 0; public synchronized void increment() throws InterruptedException { if (number != 0) { //等待 this.wait(); } number++; //通知其他线程,我+1完毕了 System.out.println(Thread.currentThread().getName()+"-->"+number); this.notifyAll(); } public synchronized void decrement() throws InterruptedException { if (number == 0) { this.wait(); } number--; System.out.println(Thread.currentThread().getName()+"-->"+number); this.notifyAll(); } }
1.6 防止虚假唤醒问题
四个线程时(两个生产者两个消费者)出现虚假唤醒问题,需要使用while来替换if来判断唤醒后是否可以继续执行
理解 : 拿两个加法线程A、B来说,比如A先执行,执行时调用了wait方法,那它会等待,此时会释放锁,那么线程B获得锁并且也会执行wait方法,两个加线程一起等待被唤醒。此时减线程中的某一个线程执行完毕并且唤醒了这俩加线程,那么这俩加线程不会一起执行,其中A获取了锁并且加1,执行完毕之后B再执行。如果是if的话,那么A修改完num后,B不会再去判断num的值,直接会给num+1。如果是while的话,A执行完之后,B还会去判断num的值,因此就不会执行
package pc; /* * 线程之间的通信问题:生产者和消费者问题 等待唤醒 ,通知唤醒 * 线程交替执行 A B 操作同一个变量 num = 0; * A num+1 * B num-1 * */ public class A { public static void main(String[] args) { Data data = new Data(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"D").start(); } } //等待 业务 通知 class Data{//数字 资源类 private int number = 0; public synchronized void increment() throws InterruptedException { while (number != 0) { //等待 this.wait(); } number++; //通知其他线程,我+1完毕了 System.out.println(Thread.currentThread().getName()+"-->"+number); this.notifyAll(); } public synchronized void decrement() throws InterruptedException { while (number == 0) { this.wait(); } number--; System.out.println(Thread.currentThread().getName()+"-->"+number); this.notifyAll(); } }
1.7 JUC 版的生产者消费者问题 Lock Condition
await:
signalAll:
package pc; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class B { public static void main(String[] args) { Data2 data = new Data2(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.increment(); } catch (InterruptedException e) { e.printStackTrace(); } } },"C").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { try { data.decrement(); } catch (InterruptedException e) { e.printStackTrace(); } } },"D").start(); } } //等待 业务 通知 class Data2 {//数字 资源类 private int number = 0; Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); // condition.await();//等待 // condition.signalAll();//唤醒全部 public void increment() throws InterruptedException { lock.lock(); try { //业务代码 while (number != 0) { //等待 condition.await(); } number++; //通知其他线程,我+1完毕了 System.out.println(Thread.currentThread().getName() + "-->" + number); condition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void decrement() throws InterruptedException { lock.lock(); try { while (number == 0) { condition.await(); } number--; System.out.println(Thread.currentThread().getName() + "-->" + number); condition.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
1.8 Condition 实现精准通知唤醒
await:
signal:
package pc; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class C { public static void main(String[] args) { Data3 data = new Data3(); new Thread(() -> { for (int i = 0; i < 10; i++) { data.printA(); } }, "A").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { data.printB(); } }, "B").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { data.printC(); } }, "C").start(); } } class Data3 {// 资源类 Lock private Lock lock = new ReentrantLock(); private Condition condition1 = lock.newCondition(); private Condition condition2 = lock.newCondition(); private Condition condition3 = lock.newCondition(); private int number = 1; public void printA() { lock.lock(); try { //业务,判断->执行->通知 while (number != 1) { //等待 condition1.await(); } System.out.println(Thread.currentThread().getName() + "--->A"); //唤醒指定的人:B number = 2; condition2.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printB() { lock.lock(); try { //业务,判断->执行->通知 while (number != 2) { //等待 condition2.await(); } System.out.println(Thread.currentThread().getName() + "--->B"); //唤醒指定的人:C number = 3; condition3.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printC() { lock.lock(); try { //业务,判断->执行->通知 while (number!=3){ //等待 condition3.await(); } System.out.println(Thread.currentThread().getName()+"--->C"); number=1; condition1.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }
1.9 八锁现象问题理解
同一把锁:synchronized 锁的对象是方法的调用者(phone)
package lock; import java.util.concurrent.TimeUnit; /* * 1.标准情况下,两个线程先打印发短信还是打电话?1.发短信﹑2.打电话 * 2.sendSms延迟4秒,两个线程先打印 发短信还是 打电话?1.发短信 2.打电话 * */ public class Demo1 { public static void main(String[] args) throws InterruptedException { Phone phone = new Phone(); //锁的存在 new Thread(()->{ phone.sendSms(); },"A").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ phone.call(); },"B").start(); } } class Phone{ //synchronized 锁的对象是方法的调用者 //两个方法是同一个锁,谁先拿到谁先执行 public synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } public synchronized void call(){ System.out.println("打电话"); } }
结果
两个对象不是用一把锁:phone,phone2
package com.example.juc.test; import java.util.concurrent.TimeUnit; /* * 3.增加了一个普通方法,先发短信还是Hello * 4.两个对象,两个同步方法,先发短信还是先打电话 * */ public class Demo1 { public static void main(String[] args) throws InterruptedException { //两个对象,两个调用者,两把锁! Phone phone = new Phone(); Phone phone2 = new Phone(); //锁的存在 new Thread(()->{ System.out.println(Thread.currentThread().getName()); phone.hello(); phone.sendSms(); },"A").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ System.out.println(Thread.currentThread().getName()); phone2.hello(); phone2.call(); },"B").start(); } } class Phone{ //synchronized 锁的对象是方法的调用者 //两个方法是同一个锁,谁先拿到谁先执行 public synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } public synchronized void call(){ System.out.println("打电话"); } //这里没有锁,不是同步方法,不受锁的影响 public void hello(){ System.out.println("hello"); } }
static静态方法两把锁锁的都是class模板:class phone
Phone唯一的一个Class对象
package com.example.juc.test; import java.util.concurrent.TimeUnit; /* * 5.增加两个静态的同步方法,只有一个对象,先打印 发短信还是打电话 * 6.两个对象!增加两个静态的同步方法, 先打印 发短信还是打电话 * */ public class Demo1 { public static void main(String[] args) throws InterruptedException { //两个对象的Class类模板只有一个,static,锁的是Class Phone phone = new Phone(); Phone phone2 = new Phone(); //锁的存在 new Thread(()->{ phone.sendSms(); },"A").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ phone2.call(); },"B").start(); } } //Phone唯一的一个Class对象 class Phone{ //synchronized 锁的对象是方法的调用者 //static 静态方法 //类一加载就有了!锁的是Class public static synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } public static synchronized void call(){ System.out.println("打电话"); } }
结果:
不是同一把锁,A线程的锁是phone类,B线程的锁是phone2
package com.example.juc.test; import java.util.concurrent.TimeUnit; /* * 7.1个静态的同步方法,1个普通的同步方法,1个对象,先打印谁 * 8.1个静态的同步方法,1个普通的同步方法,2个对象,先打印谁 * */ public class Demo1 { public static void main(String[] args) throws InterruptedException { //两个对象的Class类模板只有一个,static,锁的是Class Phone phone = new Phone(); Phone phone2 = new Phone(); //锁的存在 new Thread(() -> { phone.sendSms(); }, "A").start(); TimeUnit.SECONDS.sleep(1); new Thread(() -> { phone2.call(); }, "B").start(); } } //Phone唯一的一个Class对象 class Phone { //静态的同步方法 锁的是Class类模板 public static synchronized void sendSms() { try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } //普通的同步方法 锁的调用者 public synchronized void call() { System.out.println("打电话"); } }
结果:
总结:
- new this 具体的一个手机
- static Class 唯一的一个类模板