1.什么是JUC
java.util工具包
业务: 普通的线程代码, 之前都是用的thread或者runnable接口
但是相比于callable来说,thread没有返回值,且效率没有callable高
2. 线程和进程
线程,进程
进程 : 一个运行中的程序的集合; 一个进程往往可以包含多个线程,至少包含一个线程
java默认有几个线程? 两个 main线程 gc线程
线程 : 线程(thread)是操作系统能够进行运算调度的最小单位。
对于java而言如何创建thread: 继承自thread,实现runnable接口,实现callable接口
Java真的可以开启线程吗? 开不了的,底层是用native关键词修饰.调用本地实现
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
//本地方法,调用底层c++, java无法操作硬件
private native void start0();
并发,并行
并发编程: 并发和并行
并发(多线程操作同一个资源,交替执行)
- CPU一核, 模拟出来多条线程,天下武功,唯快不破,快速交替
并行(多个人一起行走, 同时进行)
-
CPU多核,多个线程同时进行 ; 使用线程池操作
public static void main(String[] args) {
//获取CPU核数
//CPU 密集型,IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}
并发编程的本质: 充分利用CPU的资源
所有的公司都很看重!
线程有几个状态? 6个状态
public enum State {
// 新生
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待,死死的等
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
}
wait/sleep的区
import java.util.concurrent.TimeUnit;
//企业一般用这个
TimeUnit.SECONDS.sleep(2);
-
来自不同的类
wait来自object类, sleep来自线程类
-
关于锁的释放
wait会释放锁, sleep不会释放锁
-
使用的范围不同
wait必须在同步代码块中
sleep可以在任何地方睡
-
是否需要捕获异常
wait不需要捕获异常,只要是线程都会有中断异常。
sleep需要捕获异常
3. Lock锁(重点)
传统synchronized
本质: 队列和锁,放在方法上锁的是this,放在代码块中锁的是()里面的对象
/**
* 不推荐
* 真正的多线程开发,公司中的开发
* 线程就是一个单独的资源类没有任何附属的操作!
* 1.属性、方法
*/
public class SaleTicketDemo01 {
public static void main(String[] args) {
new Thread(new MyThread()).start();
}
}
/**
* 这样完全变成线程类,没有OOP,耦合性高!
*/
class MyThread implements Runnable{
@Override
public void run() {
}
}
推荐这种
public class SaleTicketDemo01 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(() ->{
for (int i = 2; i < 60; i++) {
ticket.sale();
}
},"A").start();
new Thread(() ->{
for (int i = 2; i < 60; i++) {
ticket.sale();
}
},"B").start();
new Thread(() ->{
for (int i = 2; i < 60; i++) {
ticket.sale();
}
},"C").start();
}
}
/**
* 这样完全变成线程类,没有OOP,耦合性高!
*/
class Ticket{
//属性、方法
private int number = 50;
int i = 1;
//卖票的方式
//synchronized 本质: 队列,锁
public synchronized void sale(){
if (number > 0){
number--;
System.out.println(Thread.currentThread().getName()+"卖出了"+(i++)+"票,剩余:"+number);
}
}
}
Lock 接口
实现类
reentrantLock构造器
public ReentrantLock() {
sync = new NonfairSync(); //无参默认非公平锁
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();//传参为true为公平锁
}
公平锁: 十分公平: 先来后到,一定要排队
非公平锁: 十分不公平,可以插队(默认)
public class SaleTicketDemo {
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();
new Thread(()->{for(int i = 0; i < 40; i++) ticket.sale();}, "c").start();
}
}
class Ticket {
private int ticketNum = 30;
private Lock lock = new ReentrantLock();
public void sale() {
lock.lock();
try {
if (this.ticketNum > 0) {
System.out.println(Thread.currentThread().getName() + "购得第" + ticketNum-- + "张票, 剩余" + ticketNum + "张票");
}
//增加错误的发生几率
Thread.sleep(10);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
synchronized和lock锁的区别
- synchronized内置的java关键字,Lock是一个java类
- synchronized无法判断获取锁的状态, Lock可以判断是否获取到了锁
- synchronized会自动释放锁,Lock必须要手动释放锁!如果不是释放锁,会产生死锁
- synchronized 线程1(获得锁,阻塞),线程2(等待); Lock锁就不一定会等待下去
- synchronized 可重入锁,不可以中断的,非公平的; Lock锁,可重入的,可以判断锁,非公平(可自己设置);
- synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码
锁是什么,如何判断锁的是谁!
4. 生产者和消费者问题
面试高频: 单例模式, 八大排序,生产者消费者,死锁
Synchronized实现 wait notify
package com.czp.syncconsumer;
public class Test {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 20; i++) {
try {
data.increment();
System.out.println(Thread.currentThread().getName() + "让Num增加, num => " + data.getNum());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
for (int i = 0; i < 20; i++) {
try {
data.decrement();
System.out.println(Thread.currentThread().getName() + "让Num减少, num => " + data.getNum());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
class Data {
private int num = 0;
public int getNum() {
return num;
}
public synchronized void increment() throws InterruptedException {
if (num != 0) {
this.wait();
}
num++;
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
if (num == 0) {
this.wait();
}
num--;
this.notifyAll();
}
}
问题存在,ABCD4个线程是否安全, 不安全 会有虚假唤醒
if判断改为while判断
因为if只会执行一次,执行完会接着向下执行if()外边的
而while不会,直到条件满足才会向下执行while()外边的
JUC版本生产者和消费者问题
任何一个新的技术,绝对不是仅仅覆盖了原来的技术,一定有优势和补充
Condition 精准的通知和唤醒线程
创建不同的监视器,达到顺序执行