Juc 学习笔记
1、什么是 JUC
JUC就是 java.util
下的工具包、包、分类等。
普通的线程代码:
- Thread
- Runnable 没有返回值、效率相比入 Callable 相对较低!
- Callable 有返回值!
2、线程和进程
线程、进程,如果不能使用一句话说出来的技术,不扎实!
- 进程:一个程序,QQ.exe Music.exe 程序的集合;
- 一个进程往往可以包含多个线程,至少包含一个!
- Java默认有2个线程? mian、GC
- 线程:开了一个进程 Typora,写字,自动保存(线程负责的)
- 对于Java而言提供了:
Thread、Runnable、Callable
操作线程。
Java 真的可以开启线程吗? 答案是:开不了的!
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 thenit will be passed up the call stack */}}
}
// 本地方法,底层操作的是C++ ,Java 无法直接操作硬件
private native void start0();
并发、并行
并发编程:并发、并行
并发(多线程操作同一个资源)
- 一核CPU,模拟出来多条线程,快速交替。
并行(多个人一起行走)
- 多核CPU ,多个线程可以同时执行; eg: 线程池!
public class Test1 {public static void main(String[] args) {// 获取cpu的核数 // CPU 密集型,IO密集型 System.out.println(Runtime.getRuntime().availableProcessors());// 如果电脑是8核,则结果输出8}
}
并发编程的本质:充分利用CPU的资源
线程有几个状态(6个)
public enum State {/*** Thread state for a thread which has not yet started.* 线程新生状态*/NEW,/*** Thread state for a runnable thread. A thread in the runnable* state is executing in the Java virtual machine but it may* be waiting for other resources from the operating system* such as processor.* 线程运行中*/RUNNABLE,/*** Thread state for a thread blocked waiting for a monitor lock.* A thread in the blocked state is waiting for a monitor lock* to enter a synchronized block/method or* reenter a synchronized block/method after calling* {@link Object#wait() Object.wait}.* 线程阻塞状态*/BLOCKED,/*** Thread state for a waiting thread.* A thread is in the waiting state due to calling one of the* following methods:* <ul>* <li>{@link Object#wait() Object.wait} with no timeout</li>* <li>{@link #join() Thread.join} with no timeout</li>* <li>{@link LockSupport#park() LockSupport.park}</li>* </ul>** <p>A thread in the waiting state is waiting for another thread to* perform a particular action.** For example, a thread that has called <tt>Object.wait()</tt>* on an object is waiting for another thread to call* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on* that object. A thread that has called <tt>Thread.join()</tt>* is waiting for a specified thread to terminate.* 线程等待状态,死等*/WAITING,/*** Thread state for a waiting thread with a specified waiting time.* A thread is in the timed waiting state due to calling one of* the following methods with a specified positive waiting time:* <ul>* <li>{@link #sleep Thread.sleep}</li>* <li>{@link Object#wait(long) Object.wait} with timeout</li>* <li>{@link #join(long) Thread.join} with timeout</li>* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>* </ul>* 线程超时等待状态,超过一定时间就不再等*/TIMED_WAITING,/*** Thread state for a terminated thread.* The thread has completed execution.* 线程终止状态,代表线程执行完毕*/TERMINATED;
}
wait/sleep 区别
1、二者来自不同的类
- wait => Object
- sleep => Thread
2、关于锁的释放
- wait 会释放锁
- sleep 睡觉了,抱着锁睡觉,不会释放!
3、使用的范围是不同的
- wait 必须在同步代码块中使用
- sleep 可以再任何地方睡眠
3、Synchronized锁
传统 Synchronized锁
来看一个多线程卖票例子
package com.haust.juc01;
/** @Auther: * @Date: * @Description: 卖票例子*/
public class SaleTicketTDemo01 {/** 真正的多线程开发,公司中的开发,降低耦合性* 线程就是一个单独的资源类,没有任何附属的操作!* 1、 属性、方法*/public static void main(String[] args) {//并发:多个线程同时操作一个资源类,把资源类丢入线程Ticket ticket = new Ticket();// @FunctionalInterface 函数式接口,jdk1.8 lambada表达式new Thread(() -> {for (int i = 1; i < 50; i++) {ticket.sale();}}, "A").start();new Thread(() -> {for (int i = 1; i < 50; i++) {ticket.sale();}}, "B").start();new Thread(() -> {for (int i = 1; i < 50; i++) {ticket.sale();}}, "C").start();}
}
//资源类 OOP
class Ticket {//属性、方法private int number = 50;// 卖票的方式// synchronized 本质: 队列,锁public synchronized void sale() {if (number > 0) {System.out.println(Thread.currentThread().getName() + "卖出了" +(50-(--number)) + "张票,剩余:" + number + "张票");}}
}
4、Lock锁(重点)
Lock 接口
- 公平锁:十分公平,线程执行顺序按照先来后到顺序
- 非公平锁:十分不公平:可以插队 (默认锁)
将上面的卖票例子用lock锁 替换synchronized:
package com.haust.juc01;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/** @Auther: * @Date: * @Description: 卖票例子2*/
public class SaleTicketTDemo02 {public static void main(String[] args) {//并发:多个线程同时操作一个资源类,把资源类丢入线程Ticket2 ticket = new Ticket2();// @FunctionalInterface 函数式接口,jdk1.8 lambada表达式new Thread(() -> {for (int i = 1; i < 50; i++) {ticket.sale();}}, "A").start();new Thread(() -> {for (int i = 1; i < 50; i++) {ticket.sale();}}, "B").start();new Thread(() -> {for (int i = 1; i < 50; i++) {ticket.sale();}}, "C").start();}
}
//Lock 3步骤
// 1. new ReentrantLock();
// 2. lock.lock() 加锁
// 3. lock.unlock() 解锁
class Ticket2 {//属性、方法private int number = 50;Lock lock = new ReentrantLock();// 卖票方式public void sale() {lock.lock();// 加锁try {// 业务代码if (number > 0) {System.out.println(Thread.currentThread().getName() + "卖出了" +(50 - (--number)) + "张票,剩余:" + number + "张票");}} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();// 解锁}}
}
Synchronized 和 Lock 区别:
- 1、Synchronized 内置的Java关键字, Lock 是一个Java类
- 2、Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
- 3、Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁
- 4、Synchronized 线程 1(获得锁,如果线程1阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下去;
- 5、Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以判断锁,非公平(可以自己设置);
- 6、Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码!
锁是什么,如何判断锁的是什么!
这个问题在之后会举例分析。
5、生产者和消费者问题
面试常考的问题:单例模式、排序算法、生产者和消费者、死锁
生产者和消费者问题 Synchronized 版
package com.haust.pc;
/*** 线程之间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒* 线程交替执行 A B 操作同一个变量 num = 0* A num+1* B num-1*/
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();new Thread(() -> {for (int i = 0; i < 10; i++) {try {data.increment();} catch (InterruptedException e) {e.printStackTrace();}}}, "C").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {data.decrement();} catch (InterruptedException e) {e.printStackTrace();}}}, "D").start();}
}
// 判断等待,业务,通知
class Data {// 数字 资源类private int number = 0;//+1public synchronized void increment() throws InterruptedException {/*假设 number此时等于1,即已经被生产了产品如果这里用的是if判断,如果此时A,C两个生产者线程争夺increment()方法执行权假设A拿到执行权,经过判断number!=0成立,则A.wait()开始等待(wait()会释放锁),然后C试图去执行生产方法,但依然判断number!=0成立,则B.wait()开始等待(wait()会释放锁)碰巧这时候消费者线程线程B/D去消费了一个产品,使number=0然后,B/D消费完后调用this.notifyAll();这时候2个等待中的生产者线程继续生产产品,而此时number++ 执行了2次同理,重复上述过程,生产者线程继续wait()等待,消费者调用this.notifyAll();然后生产者继续超前生产,最终导致‘产能过剩’,即number大于1if(number != 0){// 等待this.wait();}*/while (number != 0) {// 注意这里不可以用if 否则会出现虚假唤醒问题,解决方法将if换成while// 等待this.wait();}number++;System.out.println(Thread
发布评论