JUC概述
JUC简介
- 在Java中,线程部分是一个重点,本篇文章说的JUC也是关于线程的。JUC就是java.util.concurrent工具包的简称。这是一个处理线程的包,JDK1.5开始出现的。
进程与线程
- 进程(Process)
- 计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。 在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。
- 线程(thread)
- 操作系统能够进行运算调度的最小单位。它包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务
- 进程:值在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程———资源分配的最小单位
- 线程:系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。线程———程序执行的最小单位
线程的状态
- 新建
- 就绪
- 运行
- 阻塞
- 结束
wait/sleep
- sleep是Thread的静态方法,wait是Object的方法,任何对象实例都能调用
- sleep不会释放锁,它也不需要占用锁。wait会释放锁,但调用它的前提是当前线程占有锁(即代码要在synchronized中)
- 他们都可以被interrupthttps://www.bilibili.com/account/historyed方法中断
并发与并行
- 并发
- 同一时刻多个线程在访问统一资源,多个线程对一个点
- 并行
- 多项工作一起执行,之后再汇总
卖票
/**
* 三个售票员 卖 30张票
*/
public class SaleTicket {
public static void main(String[] args) {
//线程类
final Ticket ticket = new Ticket();
/**
* 使用匿名内部类实现Runnable接口
*/
new Thread(new Runnable() {
public void run() {
while (ticket.getTicketCount() > 0){
ticket.sale();
}
}
}, "售票员1").start();
new Thread(new Runnable() {
public void run() {
while (ticket.getTicketCount() > 0){
ticket.sale();
}
}
}, "售票员2").start();
new Thread(new Runnable() {
public void run() {
while (ticket.getTicketCount() > 0){
ticket.sale();
}
}
}, "售票员3").start();
}
}
class Ticket{
private int ticketCount = 30;
public synchronized void sale(){
if (ticketCount > 0){
System.out.println(Thread.currentThread().getName()+"卖出第"+(ticketCount--)+"张票,剩余"+ticketCount+"张票");
}
}
public int getTicketCount() {
return ticketCount;
}
}
- 使用可重入锁(ReentrantLock)
class Ticket{
private int ticketCount = 30;
//可重入锁
private Lock lock = new ReentrantLock();
public void sale(){
//加锁
lock.lock();
try {
if (ticketCount > 0){
System.out.println(Thread.currentThread().getName()+"卖出第"+(ticketCount--)+"张票,剩余"+ticketCount+"张票");
}
}finally {
//解锁
lock.unlock();
}
}
public int getTicketCount() {
return ticketCount;
}
}
线程间的通信
生产者消费者通信
- 现有两个线程,可以操作初始值为0的一个变量,实现一个线程对变脸加一,一个线程实现对变量减一,实现交替来5轮
//空调
class AirConditioner{
//空调度数
private int number = 0;
//加一度
public synchronized void increment() throws InterruptedException {
//判断number是否为0,如果不为0则等待
if (number != 0){
this.wait();
}
//加一度
number++;
System.out.println(Thread.currentThread().getName()+"\t"+number);
//通知另一个线程可以减1了
this.notifyAll();
}
//减一度
public synchronized void decrement() throws InterruptedException {
if (number == 0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"\t"+number);
this.notifyAll();
}
}
/**
* 现在有两个线程,可以操作初始值为0的一个变量
* 实现一个线程对变量加1,一个线程对该变量减1
* 实现交替,来5轮
*/
public class ThreadWaitNotifyDemo {
public static void main(String[] args) {
//空调资源类
AirConditioner airConditioner = new AirConditioner();
//线程A每次加一,来10轮
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
airConditioner.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
//线程B每次减一,来10轮
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
airConditioner.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
- 控制台输出
A 1
B 0
A 1
B 0
A 1
B 0
A 1
B 0
A 1
B 0
- 新增两个线程,两个线程加一,两个线程减一。则会出现虚假唤醒的情况
- 例子:变量为0线程1判断通过则加一然后唤醒其他线程(此时应该唤醒减一的线程),唤醒了所有的线程,并且另一个加一的线程执行了,则变量变为2
- 解决方法,使用while提换掉if,当被唤醒之后再进行判断是否执行
//空调
class AirConditioner{
//空调度数
private int number = 0;
//加一度
public synchronized void increment() throws InterruptedException {
//判断number是否为0,如果不为0则等待
while (number != 0){
this.wait();
}
//加一度
number++;
System.out.println(Thread.currentThread().getName()+"\t"+number);
//通知另一个线程可以减1了
this.notifyAll();
}
//减一度
public synchronized void decrement() throws InterruptedException {
while (number == 0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"\t"+number);
this.notifyAll();
}
}
- 使用可重入锁ReentrantLock替换synchronized
//空调
class AirConditioner{
//空调度数
private int number = 0;
//可重入锁
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
//加一度
public void increment() throws InterruptedException {
//加锁
lock.lock();
try{
//判断number是否为0,如果不为0则等待
while (number != 0){
condition.await();
}
//加一度
number++;
System.out.println(Thread.currentThread().getName()+"\t"+number);
//通知另一个线程可以减1了
condition.signal();
}finally {
lock.unlock();
}
}
//减一度
public synchronized void decrement() throws InterruptedException {
//加锁
lock.lock();
try{
//判断number是否为0,如果为0则等待
while (number == 0){
condition.await();
}
//加一度
number--;
System.out.println(Thread.currentThread().getName()+"\t"+number);
//通知另一个线程可以加1了
condition.signal();
}finally {
lock.unlock();
}
}
}
精确通知顺序访问
- 案例
- 多线程之间按顺序,实现A-B-C
- A打印5次,B打印10次,C打印15次,来10轮
class ShareResource{
private int number = 1; //1 A打印 2 B打印 3 C打印
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
private Condition conditionC = lock.newCondition();
public void print5(){
lock.lock();
try{
while (number != 1){
conditionA.await();
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+(i+1));
}
//将标志位改为2B打印10次
number = 2;
//唤醒conditionB
conditionB.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print10(){
lock.lock();
try{
while (number != 2){
conditionB.await();
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+(i+1));
}
//将标志位改为2B打印10次
number = 3;
//唤醒conditionC
conditionC.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print15(){
lock.lock();
try{
while (number != 3){
conditionC.await();
}
for (int i = 0; i < 15; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+(i+1));
}
//将标志位改为2B打印10次
number = 1;
//唤醒conditionA
conditionA.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ThreadOrderAccess {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
new Thread(()->{
for (int i = 0; i < 10; i++) {
shareResource.print5();
}
},"A").start();
new Thread(()-> {
for (int i = 0; i < 10; i++) {
shareResource.print10();
}
},"B").start();
new Thread(()-> {
for (int i = 0; i < 10; i++) {
shareResource.print15();
}
},"C").start();
}
}
标签:JUC,Thread,lock,void,number,线程,public
From: https://www.cnblogs.com/blackyoumo/p/16923743.html