JUC并发编程
一、什么是JUC
JUC:java.util.concurrent,就是java的并发编程包
二、进程 线程
进程:一个程序的集合,一个进程至少包含一个线程,如打开的google浏览器
线程:具体执行命令的一系列指令,如打开一个文件,加载弹幕
Q:一个java程序包含几个线程?
A:main线程和gc线程
Q:java能否自己开启多线程?
A:不能,java也是一个程序,只能调用计算机底层方法才能实现多线程。
查看线程启动方法start()可知,该方法调用了private native void start0();
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只有1核,模拟多线程,实质是快速交替执行
Q;为什么要做高并发编程
A:充分利用CPU资源,提高效率,一个人能做的事不要用三个人
Q:怎样查看电脑CPU数
A:Runtime.getRuntime().availableProcessors()方法
public class JUCTest01 {
public static void main(String[] args) {
// new Thread(()->{
// System.out.println("hello");
// }).start();
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
Q:线程有几种状态
A:6种
/**
* A thread state. A thread can be in one of the following states:
*
* - {@link #NEW}
* A thread that has not yet started is in this state.
*
* - {@link #RUNNABLE}
* A thread executing in the Java virtual machine is in this state.
*
* - {@link #BLOCKED}
* A thread that is blocked waiting for a monitor lock
* is in this state.
*
* - {@link #WAITING}
* A thread that is waiting indefinitely for another thread to
* perform a particular action is in this state.
*
* - {@link #TIMED_WAITING}
* A thread that is waiting for another thread to perform an action
* for up to a specified waiting time is in this state.
*
* - {@link #TERMINATED}
* A thread that has exited is in this state.
*
*
*
*
* 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:
*
* - {@link Object#wait() Object.wait} with no timeout
* - {@link #join() Thread.join} with no timeout
* - {@link LockSupport#park() LockSupport.park}
*
*
* A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called Object.wait()
* on an object is waiting for another thread to call
* Object.notify() or Object.notifyAll() on
* that object. A thread that has called Thread.join()
* 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:
*
* - {@link #sleep Thread.sleep}
* - {@link Object#wait(long) Object.wait} with timeout
* - {@link #join(long) Thread.join} with timeout
* - {@link LockSupport#parkNanos LockSupport.parkNanos}
* - {@link LockSupport#parkUntil LockSupport.parkUntil}
*
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
Q:线程中的wait(),sleep()有什么区别?
A:区别如下
- 来自不同的类,wait来自Object,sleep来之Thread。
- 是否会释放锁,wait后会释放锁,sleep不会释放锁。
- 是否要捕获异常,wait不需要捕获异常,sleep需要
- 使用的位置不同,wait需要用在同步代码块中与notify/notifyall一起使用,sleep任意地方
四、Lock接口
/**
* 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();
}
小插曲
锁的地方不对导致的问题
1、A,B,C线程进来时ticketNum都是大于0的,A,B,C都可以进来。
2、当使用while循环时线程会一直执行,与for循环无关。if是判断语,for一次执行一次
lock锁的位置错误
错误原因是lock不是唯一的,每一个进来的线程都会new一个锁,相当于没锁。
正常位置
锁的地方是对的,但每次都是A线程执行,没其他线程的事
有锁时只有一个线程能够在进入,线程A进入while循环后执行后,一直卡着循环,没释放锁。没锁时都可以进入,就会有问题。加个等待时间就会看到交替执行
Q:synchronize和lock有什么区别
A:区别如下
synchronize | Lock |
---|---|
内置关键字 | java的一个类 |
无法判断锁的状态 | 可以判断是否获取到了锁 |
会自动释放锁 | 需手动释放锁,否则死锁 |
线程A获取到锁线程A阻塞,B也会阻塞 | B不一定会一直阻塞,tryLock()方法 |
可重入锁,不可中断,非公平 | 可重入锁,可中断,非公平与否可自己判断 |
使用与少量同步代码问题 | 适用于大量代码同步问题 |
五、生产者,消费者问题
package chapter04;
/**
* @Author Noperx
* @Create 2021-02-27 17:08
*/
public class JUCTest02 {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
try {
for (int i = 0; i < 30; i++) {
data.product();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
data.consume();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
data.consume();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"C").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
data.consume();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"D").start();
}
}
//资源类,属性+方法
class Data{
int num = 0;
public synchronized void product() throws InterruptedException {
while (num != 0) {
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName()+"->"+num);
notifyAll();
}
public synchronized void consume() throws InterruptedException {
while (num == 0) {
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName()+"->"+num);
notifyAll();
}
}
虚假唤醒问题
JUC的方法
package chapter04;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author Noperx
* @Create 2021-02-27 17:40
*/
public class JUCTest03 {public static void main(String[] args) {
Data02 data = new Data02();
new Thread(()->{ for (int i = 0; i < 30; i++) data.product(); },"A").start();
new Thread(()->{ for (int i = 0; i < 30; i++) data.consume(); },"B").start();
new Thread(()->{ for (int i = 0; i < 30; i++) data.product(); },"C").start();
new Thread(()->{ for (int i = 0; i < 30; i++) data.consume(); },"D").start();
}
}
//资源类,属性+方法
class Data02{
int num = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void product() {
lock.lock();
try {
while (num != 0) {
condition.await();
}
num++;
System.out.println(Thread.currentThread().getName()+"->"+num);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void consume() {
lock.lock();
try {
while (num == 0) {
condition.await();
}
num--;
System.out.println(Thread.currentThread().getName()+"->"+num);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
疑问:文档中为何new了两个Condition?
这涉及另外一种用处,顺序执行A->B->C->D
package chapter04;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author Noperx
* @Create 2021-02-27 18:13
*/
public class JUCTest04 {
public static void main(String[] args) {
Data03 data = new Data03();
new Thread(()->{ for (int i = 0; i < 30; i++) data.product(); },"A").start();
new Thread(()->{ for (int i = 0; i < 30; i++) data.transport(); },"B").start();
new Thread(()->{ for (int i = 0; i < 30; i++) data.consume(); },"C").start();
}
}
//资源类,属性+方法
class Data03{
int num = 0;
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
public void product() {
lock.lock();
try {
while (num != 0) {
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"->"+num);
num = 1;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void transport() {
lock.lock();
try {
while (num != 1) {
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"->"+num);
num = 2;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void consume() {
lock.lock();
try {
while (num != 2) {
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"->"+num);
num = 0;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
六、八种锁
一个对象,两个同步方法 |
---|
一个对象,两个同步方法,一个方法中有延迟 |
两个对象,两个同步方法,一个方法中有延迟 |
一个对象,一个同步方法,一个普通方法 |
一个对象,两个静态同步方法,一个方法中有延迟 |
两个对象,两个静态同步方法,一个方法中有延迟 |
一个对象,两个同步方法,一个方法中有延迟 |
一个对象,一个静态同步方法,一个普通方法 |
这里主要理解synchronize锁的是什么,它锁的是方法的调用者——对象本身,类本身或其他对象如"hello"字符串等
示例代码
package chapter04;
import java.util.concurrent.TimeUnit;
/**
* @Author Noperx
* @Create 2021-02-27 21:38
*/
public class JUCTest05 {
public static void main(String[] args) throws InterruptedException {
Person person = new Person();
Person person2 = new Person();
new Thread(()->{
try {
person.buyCar();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{person.buyHouse();}).start();
}
}
class Person{
public static synchronized void buyCar() throws InterruptedException {
TimeUnit.SECONDS.sleep(4);
System.out.println("买车");
}
public static synchronized void buyHouse(){
System.out.println("买房");
}
public void waitToDie(){
System.out.println("等死");
}
}
七、并发下的集合
集合类不安全
List
package chapter04;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @Author Noperx
* @Create 2021-02-27 22:07
*/
public class JUCTest06 {
public static void main(String[] args) {
//ConcurrentModificationException 并发修改异常,多线程下ArrayList不安全
/*
解决办法:
1、使用安全的集合类 Vector
2、使用集合工具类 Collections.synchronizedList()
3、使用JUC下的类 CopyOnWriteArrayList<>()
*/
List strs = new ArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
strs.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(strs);
},String.valueOf(i)).start();
}
}
}
Exception in thread "1" [af9dc, 99db6, cf297, 8595f, dbc82]
Exception in thread "3" [af9dc, 99db6, cf297, 8595f, dbc82, 22d6a]
Exception in thread "7" [af9dc, 99db6, cf297, 8595f, dbc82, 22d6a, 0ed99, f7126, 8657d]Exception in thread "0" Exception in thread "4"
[af9dc, 99db6, cf297, 8595f, dbc82, 22d6a, 0ed99, f7126]
[af9dc, 99db6, cf297, 8595f, dbc82]
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at java.util.AbstractCollection.toString(AbstractCollection.java:461)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at chapter04.JUCTest06.lambda$main$0(JUCTest06.java:23)
at java.lang.Thread.run(Thread.java:745)
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at java.util.AbstractCollection.toString(AbstractCollection.java:461)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at chapter04.JUCTest06.lambda$main$0(JUCTest06.java:23)
at java.lang.Thread.run(Thread.java:745)
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at java.util.AbstractCollection.toString(AbstractCollection.java:461)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at chapter04.JUCTest06.lambda$main$0(JUCTest06.java:23)
at java.lang.Thread.run(Thread.java:745)
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at java.util.AbstractCollection.toString(AbstractCollection.java:461)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at chapter04.JUCTest06.lambda$main$0(JUCTest06.java:23)
at java.lang.Thread.run(Thread.java:745)
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at java.util.AbstractCollection.toString(AbstractCollection.java:461)
at java.lang.String.valueOf(String.java:2994)
at java.io.PrintStream.println(PrintStream.java:821)
at chapter04.JUCTest06.lambda$main$0(JUCTest06.java:23)
at java.lang.Thread.run(Thread.java:745)
package chapter04;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @Author Noperx
* @Create 2021-02-27 22:07
*/
public class JUCTest06 {
public static void main(String[] args) {
//ConcurrentModificationException 并发修改异常,多线程下ArrayList不安全
/*
解决办法:
1、使用安全的集合类 Vector
2、使用集合工具类 Collections.synchronizedList()
3、使用JUC下的类 CopyOnWriteArrayList<>()
*/
List strs = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
strs.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(strs);
},String.valueOf(i)).start();
}
}
}
[2c222, 9e81a, 12a99, 41fee]
[2c222, 9e81a, 12a99, 41fee, 17bd8, 576ee, 254e7, d145e]
[2c222, 9e81a, 12a99, 41fee, 17bd8, 576ee, 254e7]
[2c222, 9e81a, 12a99, 41fee, 17bd8, 576ee]
[2c222, 9e81a, 12a99, 41fee, 17bd8, 576ee, 254e7, d145e]
[2c222, 9e81a, 12a99, 41fee, 17bd8, 576ee, 254e7, d145e, 8072a]
[2c222, 9e81a, 12a99, 41fee, 17bd8]
[2c222, 9e81a, 12a99, 41fee]
[2c222, 9e81a, 12a99, 41fee]
[2c222, 9e81a, 12a99, 41fee, 17bd8, 576ee, 254e7, d145e, 8072a, 174bb]
CopyOnWriteArrayList和Vector的区别,从源码分析
add方法,一个用的是synchronize关键字,一个用的Locks锁
//Vector
/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
//CopyOnWriteArrayList
public void add(int index, E element) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
if (index > len || index < 0)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+len);
Object[] newElements;
int numMoved = len - index;
if (numMoved == 0)
newElements = Arrays.copyOf(elements, len + 1);
else {
newElements = new Object[len + 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index, newElements, index + 1,
numMoved);
}
newElements[index] = element;
setArray(newElements);
} finally {
lock.unlock();
}
}
Set
package chapter04;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @Author Noperx
* @Create 2021-02-27 22:07
*/
public class JUCTest07 {
public static void main(String[] args) {
//ConcurrentModificationException 并发修改异常,多线程下HashSet不安全
/*
解决办法:
1、使用集合工具类 Collections.synchronizedSet()
2、使用JUC下的类 CopyOnWriteArraySet<>()
*/
Set strs =new CopyOnWriteArraySet<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
strs.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(strs);
},String.valueOf(i)).start();
}
}
}
HashSet本质
/**
* Constructs a new, empty set; the backing HashMap instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<>();
}
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element e to this set if
* this set contains no element e2 such that
* (e==null ? e2==null : e.equals(e2)).
* If this set already contains the element, the call leaves the set
* unchanged and returns false.
*
* @param e element to be added to this set
* @return true if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
Map
package chapter04;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @Author Noperx
* @Create 2021-02-27 22:07
*/
public class JUCTest07 {
public static void main(String[] args) {
//ConcurrentModificationException 并发修改异常,多线程下ArrayList不安全
/*
解决办法:
使用JUC下的类 ConcurrentHashMap<>()
*/
Map strs =new ConcurrentHashMap<>();
for (int i = 0; i < 50; i++) {
new Thread(()->{
strs.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
System.out.println(strs);
},String.valueOf(i)).start();
}
}
}
//实际中使用的HashMap
/**
* Constructs an empty HashMap with the default initial capacity
* (16) and the default load factor (0.75).
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
/**
* Constructs an empty HashMap with the specified initial
* capacity and load factor.
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @throws IllegalArgumentException if the initial capacity is negative
* or the load factor is nonpositive
*/
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
/*
new HashMap<>();等价于new HashMap<>(0.75f, 16)