1.进程及线程
进程:一个程序的集合,一个进程包含多个线程,至少一个。
线程:对于Java而言,Thread 、Runnable、Callable
Java是开启不了线程的,需要调用本地C++的方法启动硬件来开启线程。
2.并发及并行
并发:多线程操作同一个资源(CPU单核)
并行:多条任务一起行走(CPU多核)
获取CPU核数
并发编程本质:要充分利用CPU资源处理任务
3.线程状态
- NEW:新生状态
- BLOCKED:阻塞状态
- WAITING:等待状态
- TIME_WAITING:超时等待状态
- TERMINTED:终止状态
sleep与wait的区别
- sleep来自Thread类,wait来自Object类
- wait会释放锁,sleep不会释放锁
- wait必须在同步代码块中使用,sleep可以在任何地方使用
- wait不需要捕获异常,sleep需要捕获异常
Synchronized锁与Lock锁
以多线程卖票为例:
public class Test {
public static void main(String[] args) {
Ticket ticket=new Ticket();
new Thread(()->{
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"B").start();
}
}
class Ticket{
private int num=40;
public void sale(){
if (num>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(num--)+"的票,剩余票数"+num);
}
}
}
不难看出,多线程卖票在不加锁的情况下操作统一资源很容易出错。
解决1:传统解决方法是在同步代码中加入Sychronized锁:
运行如下:
解决2:加Lock锁
新建Lock锁,先加锁,业务代码用try catch包裹捕获异常,执行完之后在解锁,保证数据一致性。
public void sale(){
Lock lock=new ReentrantLock();
try {
lock.lock();
}catch (Exception e){
if (num>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(num--)+"的票,剩余票数"+num);
}
}
finally {
lock.unlock();
}
执行结果:
sychronized锁与Lock锁的区别
- sychronized是一个关键字,lock是一个类
- sychronized无法获取锁的状态,lock通过加锁和解锁可以
- sychronized会自动释放锁,lock需要手动解锁,更加灵活,如果不释放锁,会造成死锁
- sychronized锁阻塞时,另一个线程会一直等待下去,lock锁通过 lock.tryLock()来尝试获取锁,不会一直等待
- sychronized可重入锁 、不可中断、非公平。lock可重入锁,可以判断,通过加入参数设置是否可公平。
- sychronized适合锁少量的同步代码,lock适合大量的锁同步代码
提供者与消费者
sychronized版
package com.wang.JUC;
/**
* @author
* @Date 2022/7/9
* @apiNote
*/
public class Procom {
public static void main(String[] args) {
Provider provider=new Provider();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
provider.incr();
}catch (Exception e){
e.printStackTrace();
}
}
},"消费者A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
provider.decr();
}catch (Exception e){
e.printStackTrace();
}
}
},"消费者B").start();
}
/*
等待 业务 唤醒
*/
static class Provider{
private int num=0;
public synchronized void incr() throws InterruptedException {
if (num!=0){
this.wait();
}
num++;
System.out.println("正在执行加操作"+num);
this.notifyAll();
}
public synchronized void decr() throws InterruptedException {
if (num==0){
this.wait();
}
num--;
System.out.println("正在执行减操作"+num);
this.notifyAll();
}
}
}
执行:
若四个线程同时进行,两个线程进行加操作,两个线程进行减操作,则会出现虚假唤醒问题,即线程可以唤醒,而不会被通知。中断或者超时:
解决方法:等待应该出现在循环当中,即用while来替代if进行判断
执行解决:
JUC版本:
/**
* @author wangchangjun
* @Date 2022/7/9
* @apiNote
*/
public class Procom {
public static void main(String[] args) {
Provider provider=new Provider();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
provider.incr();
}catch (Exception e){
e.printStackTrace();
}
}
},"消费者A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
provider.decr();
}catch (Exception e){
e.printStackTrace();
}
}
},"消费者B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
provider.incr();
}catch (Exception e){
e.printStackTrace();
}
}
},"消费者C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
provider.decr();
}catch (Exception e){
e.printStackTrace();
}
}
},"消费者D").start();
}
/*
等待 业务 唤醒
*/
static class Provider{
private int num=0;
Lock lock=new ReentrantLock();
Condition condition= lock.newCondition();
public void incr() throws InterruptedException {
lock.lock();//上锁
try {
while (num!=0){
condition.await();//代替等待
}
num++;
System.out.println("正在执行加操作"+num);
condition.signalAll();//代替唤醒
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();//解锁
}
}
public void decr() throws InterruptedException {
lock.lock();
try {
while (num==0){
condition.await();
}
num--;
System.out.println("正在执行减操作"+num);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
执行:
缺陷:线程执行不是按顺序唤醒,需要通过Condition来优化
/**
* @author
* @Date 2022/7/9
* @apiNote
*/
public class Procom {
public static void main(String[] args) {
Provider provider=new Provider();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
provider.incr();
}catch (Exception e){
e.printStackTrace();
}
}
},"消费者A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
provider.decr();
}catch (Exception e){
e.printStackTrace();
}
}
},"消费者B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
provider.decr1();
}catch (Exception e){
e.printStackTrace();
}
}
},"消费者C").start();
//
// new Thread(()->{
// for (int i = 0; i < 10; i++) {
// try {
// provider.decr();
// }catch (Exception e){
// e.printStackTrace();
// }
// }
// },"消费者D").start();
}
/*
等待 业务 唤醒
*/
static class Provider{
private int num=1;
Lock lock=new ReentrantLock();
Condition conditionA= lock.newCondition();//分别监控
Condition conditionB= lock.newCondition();
Condition conditionC= lock.newCondition();
public void incr() throws InterruptedException {
lock.lock();
try {
while (num!=1){
conditionA.await();
}
num=2;
System.out.println(Thread.currentThread().getName()+"正在执行加操作"+num);
conditionB.signal();//A执行完唤醒指定线程B
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decr() throws InterruptedException {
lock.lock();
try {
while (num!=2){
conditionB.await();
}
num=3;
System.out.println(Thread.currentThread().getName()+"正在执行减操作"+num);
conditionC.signal();//B执行完唤醒指定线程C
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decr1() throws InterruptedException {
lock.lock();
try {
while (num!=3){
conditionC.await();
}
num=1;
System.out.println(Thread.currentThread().getName()+"正在执行减操作"+num);
conditionA.signal();//C线程执行完再次唤醒线程A
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
执行