1、什么是JUC?
java.util 工具包、包、分类
业务:普通的线程代码 Thread
Runnable 没有返回值、效率相比于Callable相对较低,所以开发中大多使用Callable
并发(多线程操作同一个资源)
- cpu单核,模拟出来多条线程,天下武功,唯快不破,快速交替
并行(多个人一起行走)
- cpu多核,多个线程可以同时执行; 要提高性能可以用线程池
public class Test1 { public static void main(String[] args) { // 获取cpu的核数 System.out.println(Runtime.getRuntime().availableProcessors()); } }
并发编程的本质:充分利用cpu的资源
线程的状态:(源码中)
wait和sleep的区别
1、来自不同的类
- wait => Object
- sleep => Thread
2、是否释放锁
- wait不释放锁
- sleep释放锁
3、使用的范围不同
- wait必须在同步代码块中
- sleep可以在任何地方睡
4、是否需要捕获异常
- wait不需要捕获异常
- sleep必须捕获异常(因为可能会发生超时等待的状况)
三、Lock锁(重点)
ReentrantLock的底层默认是非公平锁:(假如一个3s执行完,一个3小时执行完,如果用公平锁,3s的要等3小时)
公平锁:不能插队
非公平锁:可以插队
传统的锁Synchronized:
// 基本的卖票例子 /* * 真正的多线程开发,公司中的开发,要降低耦合性 * 线程就是一个单独的资源类,没有任何附属的操作 * 1.属性 2、方法 * */ public class SaleTicketDemo01 { public static void main(String[] args) { // 并发:多线程操作同一个资源类 Ticket ticket = new Ticket(); // 资源类 // @FunctionalInterface 函数式接口。 /* 匿名内部类,写起来比较繁琐,直接用lambda表达式 (参数)->{代码} ()是从方法体开始的 new Thread(new Runnable() { @Override public void run() { } }).start();*/ new Thread(()->{ // 多线程 for (int i = 1; i < 20; i++) { // 操作:把资源类丢入线程 ticket.sale(); } },"A").start(); new Thread(()->{ for (int i = 1; i < 20; i++) { ticket.sale(); } },"B").start(); new Thread(()->{ for (int i = 1; i < 20; i++) { ticket.sale(); } },"C").start(); } } // 资源类 用oop思想 class Ticket { // 属性方法 private int number = 30; // 卖票的方式 // synchronized本质:队列,锁 public synchronized void sale() { if (number > 0) { System.out.println(Thread.currentThread().getName() + "卖出第" + (number--) + "张票" + "还剩" + number); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
lock锁:
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; // 基本的卖票例子 /* * 真正的多线程开发,公司中的开发,要降低耦合性 * 线程就是一个单独的资源类,没有任何附属的操作 * 1.属性 2、方法 * */ public class SaleTicketDemo02 { public static void main(String[] args) { // 并发:多线程操作同一个资源类 Ticket ticket = new Ticket(); // 资源类 new Thread(()->{for (int i = 1; i < 20; i++) ticket.sale(); },"A").start(); new Thread(()->{for (int i = 1; i < 20; i++) ticket.sale(); },"B").start(); new Thread(()->{for (int i = 1; i < 20; i++) ticket.sale(); },"C").start(); } } // Lock三部曲 /* 1:new ReentrantLock(); 2:加锁lock.lock(); 3:解锁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); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
思考:lock锁和synchronized有什么区别?(记住4-5个)(想象成自动挡和手动挡)
1:synchronized是一个关键字,而lock是一个类
2:synchronized适合少量的代码,而lock适合大量的代码块
3:synchronized是自动的加锁解锁,而lock需要手动加锁解锁
4:synchronized 线程1(获得锁,阻塞)、线程2(等待,傻傻的等) lock不一定傻傻的等下去,它会尝试获取锁trylock()
5:synchronized无法获取锁的状态,而lock可以判断是否获取了锁
6:synchronized 可重入锁,不可中断的,非公平 lock 可重入锁,可以判断锁,非公平(可以自己设置)
public ReentrantLock(boolean fair) { this.sync = (ReentrantLock.Sync)(fair ? new ReentrantLock.FairSync() : new ReentrantLock.NonfairSync()); }
传统的生产者和消费者问题,虚假唤醒
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; // +1 public synchronized void increment() throws InterruptedException { if (number != 0) { // 等待 this.wait(); } Thread.sleep(100); number++; // 业务 System.out.println(Thread.currentThread().getName() + " => " + number); // 通知其它线程我+1加完了 this.notifyAll(); } // -1 public synchronized void decrement() throws InterruptedException { if (number == 0) { // 等待 this.wait(); } Thread.sleep(100); number--; System.out.println(Thread.currentThread().getName() + " => " + number); // 通知其它线程我-1加完了 this.notifyAll(); } }
两个线程的时候还正常,四个进程之后就乱套了
问题解决:
// 生产者消费者口诀 // 判断等待,业务,通知 class Data{ private int number = 0; // +1 public synchronized void increment() throws InterruptedException { while (number != 0) { // 等待 this.wait(); } Thread.sleep(100); number++; // 业务 System.out.println(Thread.currentThread().getName() + " => " + number); // 通知其它线程我+1加完了 this.notifyAll(); } // -1 public synchronized void decrement() throws InterruptedException { while (number == 0) { // 等待 this.wait(); } Thread.sleep(100); number--; System.out.println(Thread.currentThread().getName() + " => " + number); // 通知其它线程我-1加完了 this.notifyAll(); } }