JUC之java高并发-多线程学习
1. JUC定义
源码 +
官方文档
java.util工具包下的三个接口及工具类
**业务:**普通的线程代码:Thread
**Runnable接口:**没有返回值、效率相比于Callable
较低
2. 线程和进程
线程、进程使用一句话来概括
**进程:**一个程序,如QQ.exe,Music.exe程序的集合
- 一个进程往往可以包含多个线程,至少会包含一个!
- java默认有两个线程:main,GC
**线程:**比如当前开启了一个Typora进程,在其中进行打字,保存,删除等单一操作是由特定的线程负责的
- java实现线程:Thread、Runnable、Callable。
Java程序真的可以开启线程吗?看源码!
/**
* 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();
- 注意看start0()方法,是使用native关键字修饰的,这个是本地方法,不是java程序完成的,是别的C/C++程序操作的,因为java程序必须执行在JVM环境中,无法直接操作计算机硬件,所以创建线程本质上并非java程序完成!
并发、并行的区别
并发编程:并发、并行
并发:(多线程操作同一资源)
- CPU一核,模拟出来多条线程,通过快速交替执行来达到并发的目的
并行:(多个线程同时执行)
- CPU多核,多个线程可以同时执行;
线程池
。
并发编程的本质:充分利用CPU的资源!
线程的几个状态
/**
* 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 {
// 创建
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待-一直等待
WAITING,
// 超时等待-限制时间(过时不候)
TIMED_WAITING,
// 销毁
TERMINATED;
}
wait/sleep的区别
1. 来自不同的类
wait() ==>>>
Object类 sleep() ==>>>
Thread类
2. 关于锁的释放
wait
会释放锁,sleep
如同人睡着了,忘记释放锁的操作!
3.使用的范围不同
wait ==>>>
同步代码块 sleep ==>>>
任意地方
4.异常捕获
二者都需要进行中断异常捕获!
3. Lock锁(重点)
传统的synchronized锁
Lock锁
公平锁:十分公平,先来先得
非公平锁:十分不公平:可以插队(默认)
Synchronized锁和Lock锁的区别
1. Synchronized是内置的java关键字,Lock是一个java接口;
2. Synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁;
3. Synchronized会自动释放锁,Lock必须要手动释放锁,如果不释放锁,就会产生死锁;
4. Synchronized 线程(获取锁时会进入阻塞状态)、其余线程只能一直等待当前线程释放锁;Lock可以尝试获取当前锁,未必会一直等待下去;
5. Synchronized 可重入锁,不可以中断,为非公平锁;Lock为可重入锁,可以判断锁的状态,默认为非公平锁,但可以自行设置;
6. Synchronized 适合锁少量代码同步问题,Lock适合锁大量的同步代码块。
锁是什么?如何判断锁的对象是谁?
4. 生产者和消费者问题
面试程序:单例模式、八大排序算法、生产者和消费者、死锁
Synchronized wait notify版本为老版本!
问题存在,A、B两个线程执行没有任何问题;A、B、C、D多线程同时启动,还能保证线程同步吗?—虚假唤醒
- 解决:将if单词判断修改为循环判断
JUC版本的生产者和消费者问题,话不多说,见JDK源码:
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock(); try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock(); }
}
public Object take() throws InterruptedException {
lock.lock(); try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock(); }
}
}
- Synchronized版本 : wait()方法 || notify()方法
- Lock版本 : await()方法 || signal()方法
Condition接口的优势在什么地方?— 精准通知和唤醒线程
5. 8锁现象-解决判断锁的对象问题
对象–>多个/class–>唯一
-
8锁之问题一--->>标准情况下,两个线程是发短信先进行,还是打电话先进行(先执行发短信,再执行打电话)
-
八锁问题之二--->>增加了非同步方法后,线程执行不受锁的影响(先执行非同步方法); * 两个对象,两个同步方法,谁先执行,就先有锁锁住方法调用者,没有延迟的先执行
-
八锁问题之三--->>增加两个静态同步方法,静态方法在类加载就产生,为.class模板,一个类中的所有静态方法使用的是同一个 * Class模板,所以static方法会顺序执行,无论有多少个对象进行方法调用
-
八锁问题之四--->>一个普通的同步方法和一个静态的同步方法,在一个对象进行调用时; * 一个普通的同步方法和一个静态的同步方法,在两个对象进行调用时;我的都是先执行静态同步方法
小结
-
new
出来的实例,this
关键字可修饰,是一个类的具体实例; -
static
Class
模板,是一个类的唯一模板
6. 集合类不安全
List不安全
- 会造成
ConcurrentModificationException
并发修改异常!
解决方案如下:
1、List<String> list = new Vector<>();
2、List<String> list = Collections.synchronizedList(new ArrayList<>());
3、List<String> list = new CopyOnWriteArrayList<>(); 因为Vector中读写方法都为同步方法,在执行效率方面不如CopyOnWriteArrayList的非同步方法效率高,CopyOnWriteArrayList中使用的是Lock锁,对锁的状态操作比较灵活
Set不安全
解决方案如下:
1、Set<String> set = Collections.synchronizedSet(new HashSet<>()); 请爸爸来帮忙
2、Set<String> set = new CopyOnWriteArraySet<>();
hashSet
底层是什么?
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
**/
public HashSet() {
map = new HashMap<>();
}
/**
* add方法 set 的底层本质就是map, hashmap中key是无法重复的
**/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
// Dummy value to associate with an Object in the backing Map
private static final Object PRES