1. 什么是JUC
JUC是Java.util.concurrent包的简称,是Java提供用来支持高并发的工具类。
2.进程和线程回顾
进程/线程是什么?
进程:正在运行的程序,资源分配的最小单位
线程:CPU调度的最小单位
一个进程可能包含多个线程,至少包含一个。Java main,GC线程。
并发/并行是什么?
并发:多线程操作一个资源类,快速交替过程。
并行:多核CPU,同时处理多个线程。
你吃饭,吃到一半,电话来了,3种情况
1.吃完再去接电话(单线程)
2.先接电话再吃(交替,并发)
3.边吃边接电话(并发)
一个CPU的电脑不能并发执行任务
并发编程的主要目的,充分利用CPU的资源,提高性能
线程的状态
线程的状态6种
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING, // 等待
TIMED_WAITING, // 延时等待
TERMINATED; //
}
wait/sleep的区别
1.类
wait -> Object
sleep -> Thread, 谁调用的sleep方法就是谁睡觉!
例如:A调用了B的sleep方法,实际上就是A睡觉。
2.释放锁
sleep抱着锁睡觉
wait会释放锁
3.使用范围不同
wait、notify、notifyAll只能在同步方法中或者同步代码块中;
sleep可以在任意地方使用;
4.异常
sleep,必须捕获异常!
wait,不需要捕获异常!
3.Lock锁
传统的synchronized
package com.coding.demo01;
import java.util.TimerTask;
/**
*线程 操作(调用资源类暴露的方法) 资源类
*/
public class SaleTicketTest1 {
public static void main(String[] args) {
// 资源类
final SaleTicket saleTicket = new SaleTicket();
new Thread(new Runnable() {
public void run() {
for (int i = 1; i < 40; i++) {
saleTicket.saleTicket();
}
}
}, "A").start();
new Thread(new Runnable() {
public void run() {
for (int i = 1; i < 40; i++) {
saleTicket.saleTicket();
}
}
}, "B").start();
new Thread(new Runnable() {
public void run() {
for (int i = 1; i < 40; i++) {
saleTicket.saleTicket();
}
}
}, "C").start();
}
}
// 属性,和方法 高内聚
class SaleTicket{ //资源类
private int number = 30;
// 卖票方法
public synchronized void saleTicket(){
if (number>0){
System.out.println(Thread.currentThread().getName()+"卖出第"+ (number--) +"还剩下:"+number+"张票");
}
}
}
使用juc.locks包下的类操作Lock锁+Lambda表达式
package com.coding.demo01;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SaleTicketTest2 {
public static void main(String[] args) {
// 并发: 多线线程操作同一个资源类
// 资源类
SaleTicket2 saleTicket = new SaleTicket2();
// lambda表达式、链式编程、流式计算!
// lambda表达式,() -> {} 自动推断类型
// IDEA 一定要设置 JDK 版本为 1.8 版本
new Thread(() -> {
for (int i = 1; i < 40; i++) saleTicket.saleTicket();
}, "A").start();
new Thread(() -> {
for (int i = 1; i < 40; i++) saleTicket.saleTicket();
}, "B").start();
new Thread(() -> {
for (int i = 1; i < 40; i++) saleTicket.saleTicket();
}, "C").start();
}
}
// 属性,和方法 高内聚
class SaleTicket2 { //资源类
private int number = 30;
// 锁LOCK
private Lock lock = new ReentrantLock(); // 可重入
// 卖票方法
public void saleTicket() {
lock.lock(); // 加锁
try {
// 业务代码
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出第" + (number--) + "还剩下:" + number + "张票");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock(); // 解锁
}
}
}
synchronized和lock区别(自动档 手动挡)
1.synchronized关键字,Java内置的。lock是一个Java类
2.synchronized无法判断是否获取锁,lock可以判断是否获得锁
3.synchronized锁会自动释放!lock需要手动在finally释放锁,如果不释放锁,就会死锁。
4.synchronized线程1阻塞 线程2永久等待下去。lock可以lock.tryLock();//尝试获取锁,如果获取不到锁,可以结束等待
5.synchronizd可重入,不可中断,非公平得,Lock锁,可重入、可以判断、可以公平!
可重入:一个房子如果只有一个大门,只要拿到大门的钥匙,就可以进入卧室,厨房,厕所,不需要额外再获取钥匙。
公平:排队,必须先来后到!
非公平:可以插队~
4.生产者和消费者
线程间得通信、无法通信,调度线程
生产者和消费者synchronized版
package com.coding.demo02;
/**
* 题目:现在两个线程,操作一个初始值为0的变量
* 一个线程 + 1, 一个线程 -1。判断什么时候+1,什么时候-1
* 交替10 次
* 生产者消费者模式:判断 干活 通知
*/
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.increment();
} catch (Exception e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.decrement();
} catch (Exception e) {
e.printStackTrace();
}
}
},"B").start();
}
}
// 资源类 属性,方法
class Data{
private int num = 0;
// +1
public synchronized void increment() throws Exception{
//判断
if (num!=0){
this.wait();
}
// 干活
num++;
System.out.println(Thread.currentThread().getName()+"\t"+num);
// 通知
this.notifyAll();
}
// -1
public synchronized void decrement() throws Exception{
// 判断
if (num==0){
this.wait();
}
// 干活
num--;
System.out.println(Thread.currentThread().getName()+"\t"+num);
// 通知
this.notifyAll();
}
}
问题升级:防止虚假唤醒,4个线程,两个加,两个减
package com.coding.demo02;
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.increment();
} catch (Exception e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.decrement();
} catch (Exception e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.increment();
} catch (Exception e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.decrement();
} catch (Exception e) {
e.printStackTrace();
}
}
},"D").start();
}
}
// 资源类 属性,方法
class Data{
private int num = 0;
// +1
public synchronized void increment() throws Exception{
//判断 if 只判断了一次, 0 1 0 1
while (num!=0){
this.wait();
}
// 干活
num++;
System.out.println(Thread.currentThread().getName()+"\t"+num);
// 通知
this.notifyAll();
}
// -1
public synchronized void decrement() throws Exception{
// 判断
while (num==0){
this.wait(); //0
}
// 干活
num--;
System.out.println(Thread.currentThread().getName()+"\t"+num);
// 通知
this.notifyAll();
}
}
传统的,JUC!
新版生产者和消费者写法
任何一个新技术的出现,一定不仅仅是换了个马甲
package com.coding.demo02;
import sun.awt.SunHints;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// lock版生产者消费者
public class B {
public static void main(String[] args) {
// 新版
Data2 data = new Data2();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.increment();
} catch (Exception e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.decrement();
} catch (Exception e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.increment();
} catch (Exception e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.decrement();
} catch (Exception e) {
e.printStackTrace();
}
}
},"D").start();
}
}
// 资源类 属性,方法
class Data2{
private int num = 0;
// 定义锁
Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
// +1
public void increment() throws Exception{
// 加锁
lock.lock();
try {
//判断
while (num!=0){
condition.await(); //等待
}
// 干活
num++;
System.out.println(Thread.currentThread().getName()+"\t"+num);
// 通知
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 解锁
lock.unlock();
}
}
// -1
public void decrement() throws Exception{
// 加锁
lock.lock();
try {
// 判断
while (num==0){
condition.await(); //等待
}
// 干活
num--;
System.out.println(Thread.currentThread().getName()+"\t"+num);
// 通知
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 解锁
lock.unlock();
}
}
}
精确通知顺序访问 Condition
package com.coding.demo02;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 多个线程启动 A -- B -- C
* 三个线程依次打印
* A 5次
* B 10次
* C 15次
* 依次循环
*
* 精确通知线程消费
*/
public class C {
public static void main(String[] args) {
Data3 data = new Data3();
// 线程操作资源类
new Thread(()->{
for (int i = 1; i <= 10; i++) {
data.print5();
}
},"A").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
data.print10();
}
},"B").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
data.print15();
}
},"C").start();
}
}
// 资源类 属性,方法
class Data3{
private int num = 1; // A1 B2 C3
// 定义锁
Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition(); //3个判断,交替执行 A--B--C--A
private Condition condition2 = lock.newCondition(); //3个判断,交替执行 A--B--C--A
private Condition condition3 = lock.newCondition(); //3个判断,交替执行 A--B--C--A
// 3个方法、作业,合3为1
// +1
public void print5(){
// 加锁
lock.lock();
try {
//判断
while (num!=1){
condition1.await(); //等待
}
// 干活
for (int i = 1; i <=5 ; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
// 第一个线程通知第二个线程,第二个线程通知第三个.... 计数器
num=2;
// 通知第二个线程干活,指定谁干活
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 解锁
lock.unlock();
}
}
public void print10() {
// 加锁
lock.lock();
try {
//判断
while (num!=2){
condition2.await(); //等待
}
// 干活
for (int i = 1; i <=10 ; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
// 第一个线程通知第二个线程,第二个线程通知第三个.... 计数器
num=3;
// 通知第二个线程干活,指定谁干活
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 解锁
lock.unlock();
}
}
public void print15() {
// 加锁
lock.lock();
try {
//判断
while (num!=3){
condition3.await(); //等待
}
// 干活 = 业务代码
for (int i = 1; i <=15 ; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
num=1;
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 解锁
lock.unlock();
}
}
}