一 JUC概述
1 进程和线程
线程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;资源分配的最小单位
进程:系统分配处理器时间的基本单元,或者说是进程内独立执行的一个单元执行流。程序执行的最小单位
2.线程的五大状态
- NEW(新建)
- RUNNABLE(准备就绪)
- BLOCKED(阻塞)
- WAITING(不见不散)
- TIMED_WAITING(超时不候)
- TERMINATED(终结)
3.wait() 和 sleep()区别
- wait()是Object的方法,任何对象都能调用;而sleep()是Thread的静态方法。
- sleep抱锁睡觉,wait放开锁睡觉,在哪睡在哪醒
- 它们都可以被interrupted方法中断
4.并发和并行的区别
- 并行:多CPU,执行多任务,**应用:**泡方便面,电壶烧水,一边撕开调料倒入桶种
- 并发:单CPU,执行多任务。多个线程对应一个点,**应用:**春运车票,电商秒杀
5.管程
6.用户线程和守护线程
-
用户线程:自定义线程(new xxx),主线程结束,用户线程还在运行,jvm存活
package new_course; /** * @author Created by Lin Weihong * @date on 2022/5/31 19:33 */ public class Test1 { public static void main(String[] args) { new Thread(() -> { System.out.println(Thread.currentThread().getName() + "::" + Thread.currentThread().isDaemon()); while (true) { } }, "aa").start(); System.out.println(Thread.currentThread().getName()+"Over"); } } //运行结果,程序不会退出 mainOver aa::false //表示不是守护线程
-
守护线程:垃圾回收(运行在后台),没有用户线程,都是守护线程,jvm结束
/** * @author Created by Lin Weihong * @date on 2022/5/31 19:33 */ public class Test1 { public static void main(String[] args) { Thread aa = new Thread(() -> { System.out.println(Thread.currentThread().getName() + "::" + Thread.currentThread().isDaemon()); while (true) { } }, "aa"); aa.setDaemon(true);//设置为守护线程 aa.start(); System.out.println(Thread.currentThread().getName()+"Over"); } } //运行结果,程序自动退出 mainOver aa::true
二 Lock接口
1 复习Synchronized
多线程编程步骤
- 第一步 创建资源类,在资源类种创建属性和操作方法
- 第二步创建多个线程,调用资源类的操作方法
package new_course.chp2.sycn;
/**
* @author Created by Lin Weihong
* @date on 2022/5/31 20:00
*/
//1.第一步 创建资源类,在资源类种创建属性和操作方法
class Ticket {
//票数
private int number = 30;
//操作方法
public synchronized void sale() {
//判断是否有票
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + number-- + "张票,还剩下" + number);
}
}
}
public class SaleTicket {
//2.第二步创建多个线程,调用资源类的操作方法
public static void main(String[] args) {
//创建Ticket
Ticket ticket = new Ticket();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}, "aa").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}, "bb").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}, "cc").start();
}
}
2 Lock接口
Lock和Synchronized区别
- Lock不是Java语言内置的,synchronized是Java语言关键字,因此是内置特性;Lock是一个类,通过这个类可以实现同步访问
- synchronized不需要手动释放锁,当synchronized方法或者synchronized代码块执行完之后,或者出现异常,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁(在finally语句中执行),如果没有主动释放锁,可能会出现死锁现象。
- Lock可以提高多个线程进行读操作的效率。
- 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
- Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断。
在资源竞争大的时候,Lock的效率大于synchronized
Lock之卖票案例
package new_course.chp2.lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author Created by Lin Weihong
* @date on 2022/5/31 23:44
*/
//第一步:创建资源类,定义属性和操作方法
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-- + "张票,还剩下" + number);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
}
}
public class LSaleTicket {
//第二步,创建多个线程,调用资源类的操作方法
public static void main(String[] args) {
LTicket ticket = new LTicket();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"aa").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"bb").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"cc").start();
}
}
三 线程之间的通信
synchronized实现
package new_course.chp2.increment_decrement;
import sun.security.krb5.internal.Ticket;
/**
* @author Created by Lin Weihong
* @date on 2022/6/1 0:21
* 题目需求:两个线程交替+1 -1 20轮,使得结果最后仍为0
*/
public class ThreadDemo01 {
public static void main(String[] args) {
//第三步
Share share = new Share();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
//+1方法
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "aa").start();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
// -1方法
share.decr();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"bb").start();
}
}
//第一步
class Share {
private int number = 0;
public synchronized void incr() throws InterruptedException {
//第二步 判断 干活 通知
//if (number != 0) { //判断,不是0,就等待,存在虚假唤醒。
while(number != 0) {// 解决虚假唤醒
wait();
}
number++;//干活
System.out.println(Thread.currentThread().getName() + "::" + number);
//通知其他线程
notifyAll();
}
public synchronized void decr() throws InterruptedException {
//if (number != 1) {
while(number != 1) {// 解决虚假唤醒
wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "::" + number);
notifyAll();
}
}
上面的代码会存在虚假唤醒的情况,问题出在if语句,可能会有多个线程进入if语句中进行等待。
Lock实现
package new_course.chp2.increment_decrement;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author Created by Lin Weihong
* @date on 2022/6/1 18:25
*/
//创建资源类
class TShare {
//定义属性
private int number = 0;
//创建Lock
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
//操作方法
public void incr() {
lock.lock();
try {
//判断
while (number != 0) {
condition.await();
}
//干活
number++;
System.out.println(Thread.currentThread().getName() + "::" + number);
//通知
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decr() {
lock.lock();
try {
//判断
while (number != 1) {
condition.await();
}
//干活
number--;
System.out.println(Thread.currentThread().getName() + "::" + number);
//通知
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class LockDemo01 {
public static void main(String[] args) {
TShare tShare = new TShare();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
tShare.incr();
}
},"线程1").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
tShare.decr();
}
},"线程2").start();
}
}
四 线程之间的定制化通信
package new_course.chp2.increment_decrement;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author Created by Lin Weihong
* @date on 2022/6/1 20:00
* 线程之间的定制化通信
* AA打印5次 --> BB打印10次 --> CC打印15次
* 重复10轮
*/
//资源类
class LShare {
//标志位 1:AA 2:BB 3:CC
private int flag = 1;
//创建Lock锁
private final Lock lock = new ReentrantLock();
//创建三个Condition
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
//操作方法
public void print5(int loop) {
//上锁
lock.lock();
try {
//判断
while (flag != 1) {
//等待
condition1.await();
}
//干活
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + "::" + i + ":轮数:" + loop);
}
//定制化,通知
//修改标志位
flag = 2;
//只通知c2
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//操作方法
public void print10(int loop) {
//上锁
lock.lock();
try {
//判断
while (flag != 2) {
//等待
condition2.await();
}
//干活
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + "::" + i + ":轮数:" + loop);
}
//定制化,通知
//修改标志位
flag = 3;
//只通知c3
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//操作方法
public void print15(int loop) {
//上锁
lock.lock();
try {
//判断
while (flag != 3) {
//等待
condition3.await();
}
//干活
for (int i = 1; i <= 15; i++) {
System.out.println(Thread.currentThread().getName() + "::" + i + ":轮数:" + loop);
}
//定制化,通知
//修改标志位
flag = 1;
//只通知c2
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class LockDemo02 {
public static void main(String[] args) {
LShare lShare = new LShare();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
lShare.print5(i);
}
}, "线程1").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
lShare.print10(i);
}
}, "线程2").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
lShare.print15(i);
}
}, "线程3").start();
}
}