JUC入门(1)—> 前置知识
1、什么是JUC?
java.util.concurrent 在并发编程中使用的工具类
2、Java中实现多线程的三种方法,区别对比:
-
实现Runnable接口相比继承Thread类有如下优势:
-
实现Runnable接口和实现Callable接口的区别:
3、进程和线程
进程是程序的一次执行过程
是一个动态概念,是程序在执行过程中分配和管理资源的基本单位,每一个进程都有一个自己的地址空间,至少有 5 种基本状态,它们是:初始态,执行态,等待状态,就绪状态,终止状态。
线程是CPU调度和分派的基本单位,
它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
4、并发和并行
- 并发(Concurrent):指两个或多个事件在同一时间间隔内发生,即交替做不同事的能力,多线程是并发的一种形式
- 并行(Parallel):指两个或者多个事件在同一时刻发生,即同时做不同事的能力
5、线程的状态
public enum State {
// 新生
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待
WAITING,
// 超时等待
TIMED_WAITING,
// 终止
TERMINATED;
}
线程之间的通信,参考下面博客:
6、wait/sleep 区别
7、Lock 锁(重点)
package com.lixiande.practice1101;
import java.util.concurrent.locks.Lock;
/**
* @program: juc
* @description: 基础的买票程序,理解Synchronized
* @author: Mr.LiXianDe
* @create: 2020-11-01 16:17
**/
public class SellingTickets {
public static void main(String[] args) {
Ticket ticket = new Ticket();
// @FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{ 代码 }
new Thread(() -> {
for(int i = 1; i < 40;i++){
ticket.sale();
}
},"A").start();
new Thread(() -> {
for(int i = 1; i < 40;i++){
ticket.sale();
}
},"B").start();
new Thread(() -> {
for(int i = 1; i < 40;i++){
ticket.sale();
}
},"C").start();
}
}
// 资源类OOP
class Ticket{
// 属性、方法
private int numbers = 30;
// 卖票方式
// synchronized本质:队列,锁
public synchronized void sale(){
if(numbers > 0){
System.out.println(Thread.currentThread().getName()+": 卖出了第 "+(numbers--)+" 张票,剩余 "+numbers+" 张票");
}
}
}
Lock
1、什么是锁?
锁是用于控制多个线程访问共享资源的工具 。 通常,锁提供对共享资源的独占访问:一次只能有一个线程可以获取锁,并且对共享资源的所有访问都要求首先获取锁。 但是,一些锁可能允许并发访问共享资源,如ReadWriteLock的读锁
2、lock与synchronized区别
虽然synchronized方法和语句的范围机制使得使用监视器锁更容易编程,并且有助于避免涉及锁的许多常见编程错误,但是有时您需要以更灵活的方式处理锁。
例如,用于遍历并发访问的数据结构的一些算法需要使用“手动”或“链锁定”:您获取节点A的锁定,然后获取节点B,然后释放A并获取C,然后释放B并获得D等。 所述的实施方式中Lock接口通过允许获得并在不同的范围释放的锁,并允许获得并以任何顺序释放多个锁使得能够使用这样的技术。
两者的比较
1、Synchronized 内置的Java关键字, Lock 是一个Java类
2、Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
3、Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁
4、Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下
去;
5、Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以
自己设置);
6、Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!
3、如何使用?
4、锁的介绍
https://www.cnblogs.com/gxyandwmm/p/9387833.html
锁的理解(可重入)
package com.lixiande.practice1101;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @program: juc
* @description: 基础的买票程序,理解Lock
* @author: Mr.LiXianDe
* @create: 2020-11-01 16:17
**/
public class SellingTicketsPlus {
public static void main(String[] args) {
// 并发:多个线程操作同一个资源类,把资源类丢入线程
TicketAnother ticket = new TicketAnother();
new Thread(() -> {
for(int i = 1; i < 40;i++){
ticket.sale();
}
},"A").start();
new Thread(() -> {
for(int i = 1; i < 40;i++){
ticket.sale();
}
},"B").start();
new Thread(() -> {
for(int i = 1; i < 40;i++){
ticket.sale();
}
},"C").start();
}
}
// Lock三部曲
// 1、 new ReentrantLock();
// 2、 lock.lock(); // 加锁
// 3、 finally=> lock.unlock(); // 解锁
class TicketAnother{
private int numbers = 30;
Lock lock = new ReentrantLock();
public void sale(){
lock.lock(); // 进行锁定
try{
//业务代码
if(numbers > 0){
System.out.println(Thread.currentThread().getName()+": 卖出了第 "+(numbers--)+" 张票,剩余 "+numbers+" 张票");
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
生产者和消费者问题 Synchronized 版
package com.lixiande.practice1101.SynPKJUC;
/**
* @program: juc
* @description: Synchronized版本的生产者消费者问题
* @author: Mr.LiXianDe
* @create: 2020-11-01 17:01
**/
public class ProducerConsumer {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for(int i = 0;i < 10;i++){
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(() -> {
for(int i = 0;i < 10;i++){
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(() -> {
for(int i = 0;i < 10;i++){
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(() -> {
for(int i = 0;i < 10;i++){
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Data{
private int number;
public synchronized void increment() throws InterruptedException {
// if (number != 0){ 此处如果用if进行判断,则会产生虚假唤醒
while (number != 0){
// 等待操作
this.wait();
}
number++;
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();
}
}
JUC版的生产者和消费者问题
package com.lixiande.practice1101.SynPKJUC;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @program: juc
* @description: Synchronized版本的生产者消费者问题
* @author: Mr.LiXianDe
* @create: 2020-11-01 17:01
**/
public class ProducerConsumerPlus {
public static void main(String[] args) {
DataAnother data = new DataAnother();
new Thread(() -> {
for(int i = 0;i < 10;i++){
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(() -> {
for(int i = 0;i < 10;i++){
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(() -> {
for(int i = 0;i < 10;i++){
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(() -> {
for(int i = 0;i < 10;i++){
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class DataAnother{
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
while (number != 0){
// 等待操作
condition.await();
}
number++;
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 (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
Condition 精准的通知和唤醒线程
这也是Lock的优势
多个线程如何控制顺序执行
package com.lixiande.practice1101.SynPKJUC;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @program: juc
* @description: A 执行完调用B,B执行完调用C,C执行完调用A
* @author: Mr.LiXianDe
* @create: 2020-11-01 17:01
**/
public class ProducerConsumerPlusPlus {
public static void main(String[] args) {
DataAnotherAnother data = new DataAnotherAnother();
new Thread(() -> {
for(int i = 0;i < 3;i++){
try {
data.printA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(() -> {
for(int i = 0;i < 3;i++){
try {
data.printB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(() -> {
for(int i = 0;i < 3;i++){
try {
data.printC();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(() -> {
for(int i = 0;i < 3;i++){
try {
data.printD();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class DataAnotherAnother{
private int number = 1;
Lock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
Condition conditionD = lock.newCondition();
public void printA() throws InterruptedException {
lock.lock();
try {
while (number != 1){
// 等待操作
conditionA.await();
}
System.out.println(Thread.currentThread().getName()+"=> AAA");
// 通知其他线程(唤醒)
number = 2;
conditionB.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() throws InterruptedException {
lock.lock();
try {
while (number != 2){
// 等待操作
conditionB.await();
}
System.out.println(Thread.currentThread().getName()+"=> BBB");
// 通知其他线程(唤醒)
number = 3;
conditionC.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() throws InterruptedException {
lock.lock();
try {
while (number != 3){
// 等待操作
conditionC.await();
}
System.out.println(Thread.currentThread().getName()+"=> CCC");
// 通知其他线程(唤醒)
number = 4;
conditionD.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printD() throws InterruptedException {
lock.lock();
try {
while (number != 4){
// 等待操作
conditionD.await();
}
System.out.println(Thread.currentThread().getName()+"=> DDD");
// 通知其他线程(唤醒)
number = 1;
conditionA.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}