什么是JUC
Java Util Concurrent
业务:普通的线程代码不能满足
Runable 没有返回值,效率相比于Callable相对较低!
java是没权限开启线程的,调用的是底层的C++
线程的三种方式
Thread Runable Callable
并发、并行
- 并发(多个线程操作一个资源)
– CPU一核、快速交替 - 并行(多个人一起行走)
– CPU多核,多个进程一起执行(提高效率使用线程池即可)
并发编程的本质:充分利用CPU的资源
所有的公司都很看重的东西
线程有几个状态
新生、运行、阻塞、等待(死等)、超时等待(超期不等待)、终止
NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED
wati/sleep 区别
1、来自不同的类
wait => Object
sleep => Thread (sleep是线程特有的)
2、关于锁的释放
wait 会释放锁
sleep 不会释放锁(抱着锁睡)
3、使用范围不同
wait 必须在同步代码块中使用
sleep 可以在任何地方睡
3、Lock锁(重点)
lock
- 公平锁:不可插队
- 非公平锁:可以插队,JAVA默认使用这个
用lamda表达式代替Thread new Runnable写法
public class Ticket {
public static void main(String[] args) {
SaleTic saleTic = new SaleTic();
new Thread(new Runnable() {
@Override
public void run() {
saleTic.sale();
}
}).start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
saleTic.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 40; i++) {
saleTic.sale();
}
},"B").start();
}
}
// 传统锁
class SaleTic{
private int number = 50;
public synchronized void sale(){
if(number > 0 ){
System.out.println((Thread.currentThread().getName()+"卖出了"+(number--)+"张票,剩余:"+number));
}
}
}
// lock三部曲
// 1、new锁 new ReentrantLock()
// 2、加锁 lock.lock()
// 3、解锁 lock.unlock()
class SaleTic{
private int number = 50;
Lock lock = new ReentrantLock();
public void sale(){
try {
lock.lock();// 加锁
// 业务代码
if(number > 0 ){
System.out.println((Thread.currentThread().getName()+"卖出了"+(number--)+"张票,剩余:"+number));
}
} 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
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
// 判断等待、业务、通知
class Data{ // 数字资源类
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
if(number != 0){
//等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知线程+1完毕
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
if(number == 0){
//等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知线程-1完毕
this.notifyAll();
}
}
问题存在 :A B C D 4个线程!还安全么
不安全,会出现虚假唤醒情况,if会跳过判断执行wait之后的代码,while不会
处理的方法: 把if()换成while()即可解决问题
while(number == 0){
//等待
this.wait();
}
原因: 因为wait的时候是在if里面wait了,当被再次唤醒时,代码不会再次判断if,而是从if里面的wait继续向下执行,所以会出错
JUC版的生产者与消费者
JUC中的wait()和notify()代替者
Condition 对比 Synchronized 的优势/升级点
Condition可以进行精准控制唤醒,将一个lock可以加多个condition
class Data3{
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private Integer number = 1;
public void printA(){
lock.lock();
try {
//业务
while(number != 1){
condition.await();
}
System.out.println(Thread.currentThread().getName()+":AAAAAAAAAAAAA");
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
//业务
while(number != 2){
condition2.await();
}
System.out.println(Thread.currentThread().getName()+":BBB");
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
//业务
while(number != 3){
condition3.await();
}
System.out.println(Thread.currentThread().getName()+":CCC");
number = 1;
condition.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}