[Java多线程]JUC并发编程笔记
目录
背景
本笔记来自于狂神说JUC笔记。
B站link: https://www.bilibili.com/video/BV1B7411L7tE
什么是JUC
java.util.concurrent
java.util.concurrent.atomic
java.util.concurrent.locks
java.util 工具包,包,分类
Runnable
没有返回值,效率相比于 Callable 相对较低
进程和线程回顾
线程,进程,如果不能用一句话说出来的技术,不扎实
进程:一个程序,QQ.exe, Music.exe 程序的集合
一个进程往往可以额包含多个线程,至少包含一个线程
Java 默认有几个线程? 2个。 main 和 GC
线程:开了一个进程 Typora,写字,自动保存(线程负责的)
对于Java而言: Thread,Runnable, Callable
Java真的可以开启线程吗? 开不了
AtomicInteger x = new AtomicInteger();
new Thread(()->{ for (int i = 0; i < 40; ++i) System.out.println(x.getAndIncrement());}, "B").start();
thread start0()
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
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 */
}
}
}
private native void start0();
并发,并行
并发编程:并发,并行
并发(多线程操作同一个资源)
- CPU一核,模拟出来多条线程,天下武功,唯快不破,快速交替
并行(多个人一起行走)
- CPU多核,多个线程可以同时执行;线程池
private static void getAvailableProcessors() {
//获取CPU的核数
System.out.println("available processors: " + Runtime.getRuntime().availableProcessors());
}
并发编程的本质:充分利用CPU的资源
所有的公司都很看重!
企业,挣钱 => 提高效率,裁员,找一个厉害的人顶替三个不怎么样的人
人员(减),技术成本(高)
线程有几个状态 - 6个
thread state
/**
* A thread state. A thread can be in one of the following states:
* <ul>
* <li>{@link #NEW}<br>
* A thread that has not yet started is in this state.
* </li>
* <li>{@link #RUNNABLE}<br>
* A thread executing in the Java virtual machine is in this state.
* </li>
* <li>{@link #BLOCKED}<br>
* A thread that is blocked waiting for a monitor lock
* is in this state.
* </li>
* <li>{@link #WAITING}<br>
* A thread that is waiting indefinitely for another thread to
* perform a particular action is in this state.
* </li>
* <li>{@link #TIMED_WAITING}<br>
* A thread that is waiting for another thread to perform an action
* for up to a specified waiting time is in this state.
* </li>
* <li>{@link #TERMINATED}<br>
* A thread that has exited is in this state.
* </li>
* </ul>
*
* <p>
* A thread can be in only one state at a given point in time.
* These states are virtual machine states which do not reflect
* any operating system thread states.
*
* @since 1.5
* @see #getState
*/
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 可以在任何地方睡
4. 是否需要捕获异常
wait 不需要捕获异常
sleep 必须要捕获异常
Lock锁
传统 Synchronized
/**
* 真正的多线程开发,公司中的开发,降低耦合性
* 线程就是一个单独的资源类,没有任何附属的操作!
* 1、 属性、方法
*/
public class SaleTicketDemo01 {
public static void main(String[] args) {
// 并发:多线程操作同一个资源类, 把资源类丢入线程
Ticket ticket = new Ticket();
// @FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{ 代码 }
new Thread(()->{ for (int i = 0; i < 5; ++i) ticket.sale();}, "threadA").start();
new Thread(()->{ for (int i = 0; i < 2; ++i) ticket.sale();}, "threadB").start();
new Thread(()->{ for (int i = 0; i < 5; ++i) ticket.sale();}, "threadC").start();
}
}
// 资源类 OOP
class Ticket {
// 属性、方法
private int number = 10;
// 卖票的方式
// synchronized 本质: 队列,锁
public synchronized void sale() {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + ": sold number " + number-- + "th, left: " + number);
}
}
}
Lock 接口
Lock l = ...;
l.lock();
try {
// access the resource proteced by this lock
} finally {
l.unlock();
}
可重入锁(常用)
ReentrantLock
ReentrantReadWriteLock.ReadLock // 读锁
ReentrantREadWriteLock.WriteLock // 写锁
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync(); // 非公平锁
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) { // 用构造参数判读是否是公平锁
sync = fair ? new FairSync() : new NonfairSync(); // FairSync() 是公平锁
}
公平锁: 十分公平:可以先来后到
非公平锁: 十分不公平: 可以插队(默认)
// Lock三部曲
// 1、 new ReentrantLock();
// 2、 lock.lock(); // 加锁
// 3、 finally=> lock.unlock(); // 解锁
public class SaleTicketDemo02 {
public static void main(String[] args) {
// 并发:多线程操作同一个资源类, 把资源类丢入线程
Ticket2 ticket2 = new Ticket2();
// @FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{ 代码 }
new Thread(()->{ for (int i = 0; i < 5; ++i) ticket2.sale();}, "threadA").start();
new Thread(()->{ for (int i = 0; i < 2; ++i) ticket2.sale();}, "threadB").start();
new Thread(()->{ for (int i = 0; i < 5; ++i) ticket2.sale();}, "threadC").start();
}
}
// Lock三部曲
// 1、 new ReentrantLock();
// 2、 lock.lock(); // 加锁
// 3、 finally=> lock.unlock(); // 解锁
class Ticket2 {
private int number = 10;
Lock lock = new ReentrantLock();
public void sale() {
lock.lock(); // 加锁
try {
// 业务代码
if (number > 0) {
System.out.println(Thread.currentThread().getName() + ": sold number " + number-- + "th, left: " + 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 适合锁大量的同步代码
生产者和消费者
面试的:单例模式,排序算法,生产者和消费者,死锁
生产者和消费者问题的 Synchronized 版本
public class PcDemo1 {
public static void main(String[] args) {
Product1 product = new Product1();
new Thread(()->{
for (int i = 0; i < 10; ++i) {
try {
TimeUnit.SECONDS.sleep(2);
product.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "threadA").start();
new Thread(()->{
for (int i = 0; i < 10; ++i) {
try {
product.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "threadB").start();
new Thread(()->{
for (int i = 0; i < 10; ++i) {
try {
TimeUnit.SECONDS.sleep(2);
product.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "threadC").start();
new Thread(()->{
for (int i = 0; i < 10; ++i) {
try {
product.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "threadD").start();
}
}
class Product1 {
private int number = 0;
// 如果没有 synchronized 关键字, IllegalMonitorStateException
public synchronized void increment() throws InterruptedException {
// 这里如果用 if 的话,会有虚假唤醒的现象
// https://www.cnblogs.com/jichi/p/12694260.html
while (number != 0) {
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
this.notifyAll();
}
// 如果没有 synchronized 关键字, IllegalMonitorStateException
public synchronized void decrement() throws InterruptedException {
// 这里如果用 if 的话,会有虚假唤醒的现象
// https://www.cnblogs.com/jichi/p/12694260.html
while (number == 0) {
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
this.notifyAll();
}
}
JUC 版本的生产者和消费者的问题
Synchorinzed: wait, notify
Lock: await, signal
public class PcDemo2 {
public static void main(String[] args) {
Product2 product2 = new Product2();
new Thread(()->{
for (int i = 0; i < 10; ++i) {
product2.increment();
}
}, "A").start();
new Thread(()->{
for (int i = 0; i < 10; ++i) {
product2.decrement();
}
}, "B").start();
new Thread(()->{
for (int i = 0; i < 10; ++i) {
product2.increment();
}
}, "C").start();
new Thread(()->{
for (int i = 0; i < 10; ++i) {
product2.decrement();
}
}, "D").start();
}
}
// 判断等待,业务,通知
class Product2 { // 数字 资源类
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// condition.await(); //等待
// condition.signalALl(); // 唤醒全部
public void increment() {
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 decrement() {
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();
}
}
}
任何一个新的技术,绝对不是仅仅只是覆盖了原来的技术,优势和补充!