目录
1 什么是JUC?
1.1 JUC简介
在Java中JUC就是java.uitl.concurrent工具包的简称。这是一个处理线程的工具包JDK1.5后出现的。
1.2 进程和线程的概念
进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。进程是程序的实体。
线程:线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
1.3 线程的状态
1.3.1 线程的状态:
new(新建),Runnable(准备就绪),Blocked(阻塞),Waiting(等待),TimeWaiting(过时不候),Terminated(终结)
1.3.2 wait/sleep的区别
(1)sleep是Thread的静态方法,wait是Object的方法任何实例对象都能调用。
(2)sleep不会释放锁,它也不需要占用锁。wait会释放锁,但调用它的前提是当前线程占有锁(即代码要在synchronized中)。
(3)它们都可以被interrupt打断
1.4 并发与并行
1.4.1 串行模式
串行模式:表示所有的任务都按先后顺序执行。
1.4.2 并行模式
并行模式:同时取得多个任务,并可以同时执行这些所取得的任务。并行模式相当于一条长长的队列。
1.4.3 并发
并发:同一时刻多个线程访问一个资源,如:春运抢票,秒杀抢购
1.4.4 并行
并行:多项工作一起执行,之后再汇总。
管程:Monitor监视器(锁)
监视器:是一种同步机制,保证同一个时间内只有一个线程去访问被保护的数据或代码。
1.4.5 用户线程和守护线程
用户线程:自定义线程(主程序结束了,用户线程还在运行,JVM存活)
public class Main {
public static void main(String[] args) {
Thread a = new Thread(() -> {
System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().isDaemon());
while (true){
}
},"a");
a.start();
System.out.println(Thread.currentThread().getName()+"over");
}
}
守护线程:(没有用户线程,都是守护线程,JVM结束运行)
package com.workspace.main;
public class Main {
public static void main(String[] args) {
Thread a = new Thread(() -> {
System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().isDaemon());
while (true){
}
},"a");
a.setDaemon(true); //设置守护线程
a.start();
System.out.println(Thread.currentThread().getName()+"over");
}
}
2 LOCK接口
2.1 synchronized
2.1.1 synchronized关键字
synchronized是Java中一个重要的关键字,是一种同步锁。
它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
2.2.2 售票员买票
public class SaleTicket {
//1.创建资源类 定义属性和操作方法
static class Ticket{
// 票数
private int number = 30;
public synchronized void sale(){
//判断是否有票
if (number > 0) {
System.out.println(Thread.currentThread().getName()+"卖出,还剩"+ --number);
}
}
}
public static void main(String[] args) {
// 创建ticket对象
Ticket ticket = new Ticket();
// 创建3个线程,分别售票
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();
}
}
2.2 LOCK接口
为锁和等待条件提供一个框架的接口和类,不同于内置同步和监视器, LOCK是类,可通过类实现同步访问,多个接口实现类:可重入锁等
可重入锁的代码定义 :ReentrantLock
private final ReentrantLock lock = new ReentrantLock(true);
用lock实现卖票操作
public class LsaleTicket {
//1.创建资源类 定义属性和操作方法
static class LTicket{
// 票数
private int number = 30;
// 创建可重入锁
private final ReentrantLock lock = new ReentrantLock();
// 买票方法
public void sale(){
// 上锁
lock.lock();
try{
//判断是否有票
if (number > 0) {
System.out.println(Thread.currentThread().getName()+"卖出,还剩"+ --number);
}
} finally{
// 解锁
lock.unlock();
}
}
}
public static void main(String[] args) {
// 创建ticket对象
LTicket ticket = new LTicket();
// 创建3个线程,分别售票
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();
}
}
2.3 Lock接口
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
2.4 synchronized与lock的差异
synchronized是java中内置关键字,而lock是一个类,可以实现同步访问且比synchronized中的方法更加丰富
synchronized不会手动释放锁,而lock需手动释放锁(不解锁会出现死锁,需要在 finally 块中释放锁)
lock等待锁的线程会相应中断,而synchronized不会相应,只会一直等待
通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到
当多个线程竞争的时候,Lock 可以提高多个线程进行读操作的效率
3 线程间的通信
线程间通信的模型有两种:共享内存和消息传递
线程间的通信具体步骤:
- 创建资源类,在资源类中创建属性和操作方法
- 在资源类操作方法:判断、操作、通知
- 创建多个线程,调用资源类的操作方法
3.1 synchronized案例
操作线程的时候,等待线程使用wait()
通知另外的线程操作用notify()、notifyAll()
假设有两个线程,该线程在执行过程中,判断值(不是该值等待,让其他线程抢),操作值,通知另外一个线程的调度
通过使用两个线程对0这个值操作,一个线程加1,一个线程减1,交替实现多次
notify()、notifyAll()的区别
notify():唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程,选择是任意性的。
notifyAll():唤醒在此对象监视器上等待的所有线程。
public class ThreadDemo1 {
// 第一步创建资源类,定义属性和方法
static class Share{
// 初始值
private int number = 0;
// 数值+1方法
public synchronized void incr() throws InterruptedException {
// 第二步 判断如果不为0则等待
if (number != 0) {
this.wait();// 哪里沉睡,哪里醒来
}
// 数值为0,就+1
number++;
System.out.println(Thread.currentThread().getName()+"数值为:"+ number);
// 通知其他线程
this.notifyAll();
}
// 数值-1方法
public synchronized void decr() throws InterruptedException {
// 第二步 判断如果不为1则等待
if (number != 1) {
this.wait();// 哪里沉睡,哪里醒来
}
// 数值为1,就-1
number--;
System.out.println(Thread.currentThread().getName()+"数值为:"+ number);
// 通知其他线程
this.notifyAll();
}
}
public static void main(String[] args) {
Share share = new Share();
new Thread(()->{
for (int i = 0; i < 10 ; i++) {
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10 ; i++) {
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();;
new Thread(()->{
for (int i = 0; i < 10 ; i++) {
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();;
new Thread(()->{
for (int i = 0; i < 10 ; i++) {
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();;
}
}
会出现虚拟唤醒导致
// 第二步 判断如果不为0则等待
if (number != 0) {
this.wait(); // 哪里沉睡,哪里醒来
}
解决方案把if变为while每次都要执行判断
while (number != 0) {
this.wait(); //哪里沉睡,哪里醒来
}
执行结果
3.2 Lock 案例
public class ThreadDemo2 {
// 第一步创建资源类,定义属性和方法
static class Share{
private int number = 0;
// 创建lock锁
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
// 数值+1方法
public void incr() throws InterruptedException {
// 上锁
lock.lock();
try {
while(number != 0){
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"数值为:"+ number);
// 通知
condition.signalAll(); // signalAll()唤醒所有等待线程;signal()唤醒一个等待线程随机
} finally {
lock.unlock();
}
}
// 数值-1方法
public void decr() throws InterruptedException {
// 上锁
lock.lock();
try {
while (number != 1){
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"数值为:"+ number);
// 通知
condition.signalAll(); // signalAll()唤醒所有等待线程;signal()唤醒一个等待线程随机
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
Share share = new Share();
new Thread(()-> {
for (int i = 0; i < 10 ; i++) {
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()-> {
for (int i = 0; i < 10 ; i++) {
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()-> {
for (int i = 0; i < 10 ; i++) {
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()-> {
for (int i = 0; i < 10 ; i++) {
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}