JavaSE# 筆記【高併發和執行緒安全 volatile關鍵字 原子類 多行程式碼的執行緒安全問題 併發包】@Gray
一.高併發和執行緒安全
1.高併發和執行緒安全
高併發:就是在一段時間內有大量的執行緒要執行. 雙11,春運12306,大學選選修課
執行緒安全:在高併發的情況下,多個執行緒之間有互相影響的效果。
2.多執行緒記憶體執行機制
當一個執行緒啟動後,JVM會為其分配一個獨立的"執行緒棧區",這個執行緒會在這個獨立的棧區中執行。
- 看一下簡單的執行緒的程式碼:
1.一個執行緒類:
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("i = " + i);
}
}
}
- 測試類:
public class Demo {
public static void main(String[] args) {
//1.建立兩個執行緒物件
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
//2.啟動兩個執行緒
t1.start();
t2.start();
}
}
- 啟動後,記憶體的執行機制
3.安全性問題-可見性
- 介紹
多個執行緒在執行的過程中,一個執行緒可能看不到另一個執行緒對變數的改變,這個叫執行緒的可見性問題. - 程式碼演示
public class AAA extends Thread {
//定義變數
static int num = 0;
@Override
public void run() {
System.out.println("AAA執行緒開始執行了");
//睡眠2秒鐘
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
num = 10;
System.out.println("AAA執行結束了");
}
}
public class BBB extends Thread{
@Override
public void run() {
//迴圈
while(true){
//判斷
if(AAA.num == 10){
System.out.println("BBB執行緒知道數字是10了");
break;
}
}
}
}
public class Test01 {
public static void main(String[] args) {
AAA a = new AAA();
a.start();
BBB b = new BBB();
b.start();
}
}
圖解:
4.安全性問題-有序性
-
介紹
-
在程式的編譯期間,程式可能會把沒有上下邏輯關係的程式碼上下打亂順序這個叫程式碼重排,一個執行緒程式碼的重排可能會對別的執行緒造成影響。
-
這是一個小概率事件所以無法通過程式碼演示。
-
-
圖解
5.安全性問題-原子性
-
介紹
一個不可分割的語句,在多執行緒的情況下被分割成了多個步驟,導致執行緒之間互相有影響。
-
程式碼演示
public class CCC extends Thread {
static int num = 0;
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
num++;
}
}
}
public class Test02 {
public static void main(String[] args) throws InterruptedException {
//開啟執行緒
CCC c1 = new CCC();
c1.start();
CCC c2 = new CCC();
c2.start();
//讓主執行緒睡眠,為了讓迴圈先執行
Thread.sleep(2000);
//列印num
System.out.println(CCC.num);
}
}
- 圖解
二.volatile關鍵字
可以解決可見性
和有序性
問題。用關鍵字修飾變數即可。
關鍵字可以讓執行緒每次都獲取最新值,並且不允許程式碼重排。
//定義變數
static volatile int num = 0;
三.原子類
1.AtomicInteger演示
原子類可以解決可見性 有序性 和 原子性
建立執行緒:
import java.util.concurrent.atomic.AtomicInteger;
public class CCC extends Thread {
//static int num = 0;
static AtomicInteger num = new AtomicInteger(0);
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
//num++;
num.getAndIncrement();
}
}
}
測試類:
public class Test02 {
public static void main(String[] args) throws InterruptedException {
//開啟執行緒
CCC c1 = new CCC();
c1.start();
CCC c2 = new CCC();
c2.start();
//讓主執行緒睡眠,為了讓迴圈先執行
Thread.sleep(2000);
//列印num
System.out.println(CCC.num);
}
}
2.AtomicInteger工作機制-CAS機制
3.AtomicIntegerArray類示例[瞭解]
陣列儲存的元素在多執行緒的情況下也會有執行緒安全問題。
執行緒類:
import java.util.concurrent.atomic.AtomicIntegerArray;
public class DDD extends Thread {
//static int[] arr = new int[1000];
//原子類陣列代替普通數字,小括號裡面寫的也是陣列長度
static AtomicIntegerArray arr = new AtomicIntegerArray(1000);
@Override
public void run() {
//1000迴圈
//0 0 0 0 0 0 0 0 0 0 0 0 0
//1 1 1 1 1 1 1 1 1 1 1 1 1
//1000 1000 1000 1000 1000 1000
for (int i = 0; i < 1000; i++) {
//arr[i]++;
//給i索引的元素加一
arr.addAndGet(i,1);
}
}
}
測試類
import java.util.Arrays;
public class Test03 {
public static void main(String[] args) throws InterruptedException {
//開啟了100個執行緒
for (int i = 0; i < 1000; i++) {
DDD d = new DDD();
d.start();
}
//睡眠2秒鐘
Thread.sleep(2000);
//列印陣列
//System.out.println(Arrays.toString(DDD.arr));
System.out.println(DDD.arr);
}
}
四.多行程式碼的執行緒安全問題【重點】
1.執行順序的問題
- 火車賣票問題
執行緒類:
public class AAA implements Runnable {
//定義火車站一共有100張票
int ticket = 100;
@Override
public void run() {
//迴圈
while(true){
//判斷有沒有票
if(ticket <= 0){
break;
}
//如果有票就賣票
System.out.println(Thread.currentThread().getName() + "賣出了" + ticket + "號票");
//給票減一
ticket--;
}
}
}
測試類:
public class Test01 {
public static void main(String[] args) {
AAA a = new AAA();
//開啟執行緒
Thread t1 = new Thread(a);
t1.setName("視窗一");
t1.start();
//開啟執行緒
Thread t2 = new Thread(a);
t2.setName("視窗二");
t2.start();
}
}
- 圖解
2.synchronized關鍵字
表示同步,同步的意思是一個執行緒在執行的時候,別的執行緒只能等待。一個執行緒執行結束之後別的執行緒才能執行。
3.同步程式碼塊
-
格式
synchronized(鎖物件){ 同步程式碼 }
-
同步鎖
- 鎖物件可以是任意型別的物件
- 多個執行緒如果要同步必須使用同一個物件作為鎖
-
程式碼演示
執行緒實現類:
public class AAA implements Runnable {
//定義火車站一共有100張票
int ticket = 100;
@Override
public void run() {
//迴圈
while(true){
//同步程式碼塊
synchronized ("abc") {
//判斷有沒有票
if (ticket <= 0) {
break;
}
//先獲取當前執行緒物件,再獲取執行緒名字
String name = Thread.currentThread().getName();
//如果有票就賣票
System.out.println(name + "賣出了" + ticket + "號票");
//給票減一
ticket--;
}
}
}
}
測試類:
public class Test01 {
public static void main(String[] args) {
AAA a = new AAA();
//開啟執行緒
Thread t1 = new Thread(a);
t1.setName("視窗一");
t1.start();
//開啟執行緒
Thread t2 = new Thread(a);
t2.setName("視窗二");
t2.start();
}
}
4.同步方法
-
格式
public synchronized void method(){ 同步程式碼 }
-
同步方法的鎖物件
- 同步方法裡面也是有鎖物件的,但是鎖物件不需要我們指定,同步方法的鎖物件是固定的。
- 非靜態同步方法:
this
(代表當前類的物件) - 靜態同步方法: 類的位元組碼物件(每一個類只有一個
.class
物件,所以這個物件一定是唯一的)
-
程式碼演示
執行緒實現類:
public class AAA implements Runnable {
//定義火車站一共有100張票
int ticket = 100;
@Override
public void run() {
//迴圈
while(true){
//判斷有沒有票
if(ticket <= 0){
break;
}
//呼叫方法
method();
}
}
//定義同步方法
public synchronized void method(){
//再次判斷
if(ticket > 0) {
//先獲取當前執行緒物件,再獲取執行緒名字
String name = Thread.currentThread().getName();
//如果有票就賣票
System.out.println(name + "賣出了" + ticket + "號票");
//給票減一
ticket--;
}
}
}
測試類:
public class Test01 {
public static void main(String[] args) {
AAA a = new AAA();
//開啟執行緒
Thread t1 = new Thread(a);
t1.setName("視窗一");
t1.start();
//開啟執行緒
Thread t2 = new Thread(a);
t2.setName("視窗二");
t2.start();
}
}
5.Lock鎖
Lock鎖的方式更符合物件導向的呼叫方式。更符合程式設計師的寫程式碼習慣。
Lock是一個介面,有一個子類ReentrantLock
- 兩個方法
public void lock() :加同步鎖
public void unlock() :釋放同步鎖
- 程式碼演示
執行緒實現類:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class AAA implements Runnable {
//定義火車站一共有100張票
int ticket = 100;
//建立鎖物件
Lock lock = new ReentrantLock();
public void run() {
//迴圈
while(true){
//加鎖
lock.lock();
//判斷有沒有票
if(ticket <= 0){
lock.unlock();
break;
}
//先獲取當前執行緒物件,再獲取執行緒名字
String name = Thread.currentThread().getName();
//如果有票就賣票
System.out.println(name + "賣出了" + ticket + "號票");
//給票減一
ticket--;
//解鎖
lock.unlock();
}
}
}
測試類:
public class Test01 {
public static void main(String[] args) {
AAA a = new AAA();
//開啟執行緒
Thread t1 = new Thread(a);
t1.setName("視窗一");
t1.start();
//開啟執行緒
Thread t2 = new Thread(a);
t2.setName("視窗二");
t2.start();
}
}
五.併發包
之前學習的集合型別都是會有執行緒安全問題的,如果遇到了多執行緒情況的,需要用併發包解決問題。
1.CopyOnWriteArrayList[瞭解]
- ArrayList和CopyOnWriteArrayList效果演示
執行緒實現類:
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArrayList;
public class AAA extends Thread {
//定義集合
//static ArrayList<Integer> list = new ArrayList<>();
//使用併發包集合
static CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
@Override
public void run() {
//給集合新增10000個元素
for (int i = 0; i < 10000; i++) {
list.add(i);
}
}
}
測試類:
public class Test01 {
public static void main(String[] args) throws InterruptedException {
//開啟執行緒
new AAA().start();
new AAA().start();
//讓主執行緒睡眠讓迴圈先執行
Thread.sleep(2000);
//列印集合長度
System.out.println(AAA.list.size());
//出現問題一:可能長度小於20000
//出現問題二:可能出現索引越界異常
}
}
2.CopyOnWriteArraySet[瞭解]
- HashSet和CopyOnWriteArraySet效果演示
執行緒實現類:
import java.util.HashSet;
import java.util.concurrent.CopyOnWriteArraySet;
public class BBB extends Thread {
//定義集合
//static HashSet<Integer> set = new HashSet<>();
//定義併發包集合
static CopyOnWriteArraySet<Integer> set = new CopyOnWriteArraySet<>();
@Override
public void run() {
//給集合新增10000個元素
for (int i = 0; i < 10000; i++) {
set.add(i);
}
}
}
測試類:
public class Test02 {
public static void main(String[] args) throws InterruptedException {
//開啟執行緒
new BBB().start();
new BBB().start();
//讓主執行緒睡眠讓迴圈先執行
Thread.sleep(2000);
//列印集合長度
System.out.println(BBB.set.size());
//出現問題:集合的長度大於10000
}
}
3.ConcurrentHashMap
1. HashMap和Hashtable和ConcurrentHashMap效果演示
執行緒實現類:
import java.util.HashMap;
import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;
public class CCC extends Thread {
//建立集合
//static HashMap<Integer,Integer> map = new HashMap<>();
//使用Hashtable集合
//static Hashtable<Integer,Integer> map = new Hashtable<>();
//使用ConcurrentHashMap集合
static ConcurrentHashMap<Integer,Integer> map = new ConcurrentHashMap<>();
@Override
public void run() {
//給集合新增10000個元素
for (int i = 0; i < 10000; i++) {
map.put(i,i);
}
}
}
測試類:
public class Test03 {
public static void main(String[] args) throws InterruptedException {
//開啟執行緒
new CCC().start();
new CCC().start();
//讓主執行緒睡眠
Thread.sleep(2000);
//列印集合長度
System.out.println(CCC.map.size());
//出現問題:集合的長度大於10000
}
}
2.Hashtable和ConcurrentHashMap的速度區別【重點】
Hashtable
執行速度慢ConcurrentHashMap
執行速度快
執行緒實現類:
import java.util.HashMap;
import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;
public class CCC extends Thread {
//建立集合
//static HashMap<Integer,Integer> map = new HashMap<>();
//使用Hashtable集合
// static Hashtable<Integer,Integer> map = new Hashtable<>();
//使用ConcurrentHashMap集合
static ConcurrentHashMap<Integer,Integer> map = new ConcurrentHashMap<>();
@Override
public void run() {
//獲取系統當前時間
long time1 = System.currentTimeMillis();
//給集合新增10000個元素
for (int i = 0; i < 10000; i++) {
map.put(i,i);
}
//獲取系統當前時間
long time2 = System.currentTimeMillis();
System.out.println( (time2-time1) + "毫秒");
}
}
測試類:
public class Test04 {
public static void main(String[] args) {
//建立1000個執行緒
for (int i = 0; i < 1000; i++) {
new CCC().start();
}
}
}
3.速度區別的原因
- Hashtable方法都是同步的,一個執行緒在執行的時候,別的執行緒只能等待。
public synchronized V put(K key, V value) {
- ConcurrentHashMap用到了CAS機制和部分同步程式碼塊.
4.悲觀鎖和樂觀鎖
- CAS機制稱為
樂觀鎖
,執行效率高
- 同步機制稱為
悲觀鎖
,執行效率低
相關文章
- 多執行緒與高併發(二)執行緒安全執行緒
- 併發程式設計之多執行緒執行緒安全程式設計執行緒
- Java併發-執行緒安全的集合類Java執行緒
- 多執行緒併發安全問題詳解執行緒
- Volatile關鍵字與執行緒安全執行緒
- 併發與多執行緒之執行緒安全篇執行緒
- 多執行緒與高併發(三)synchronized關鍵字執行緒synchronized
- Java併發專題(二)執行緒安全Java執行緒
- 併發程式設計與執行緒安全程式設計執行緒
- Java併發實戰一:執行緒與執行緒安全Java執行緒
- Java併發程式設計之執行緒安全、執行緒通訊Java程式設計執行緒
- 多執行緒與高併發(五)final關鍵字執行緒
- 【多執行緒與高併發 2】volatile 篇執行緒
- 【多執行緒與高併發】- 淺談volatile執行緒
- 多執行緒程式設計,處理多執行緒的併發問題(執行緒池)執行緒程式設計
- 10、Java併發性和多執行緒-執行緒安全與不可變性Java執行緒
- 【重學Java】多執行緒進階(執行緒池、原子性、併發工具類)Java執行緒
- 啃碎併發(五):Java執行緒安全特性與問題Java執行緒
- Java併發程式設計序列之執行緒間通訊-synchronized關鍵字-volatile關鍵字Java程式設計執行緒synchronized
- 執行緒安全(上)--徹底搞懂volatile關鍵字執行緒
- 執行緒安全(上)–徹底搞懂volatile關鍵字執行緒
- 併發程式設計之執行緒安全性程式設計執行緒
- Java多執行緒和併發問題集Java執行緒
- 多執行緒系列(十六) -常用併發原子類詳解執行緒
- Java併發(一)----程式、執行緒、並行、併發Java執行緒並行
- Android高併發問題處理和執行緒池ThreadPool執行緒池原始碼分析Android執行緒thread原始碼
- Java 多執行緒併發程式設計之 Synchronized 關鍵字Java執行緒程式設計synchronized
- Java執行緒的併發工具類Java執行緒
- java多執行緒與併發 - 併發工具類Java執行緒
- 【JAVA併發第四篇】執行緒安全Java執行緒
- JAVA 併發之路 (二) 執行緒安全性Java執行緒
- HashMap多執行緒併發問題分析HashMap執行緒
- 多執行緒併發常見問題執行緒
- 深入理解Java多執行緒與併發框(第⑦篇)——volatile 關鍵字Java執行緒
- Java併發(十七)----變數的執行緒安全分析Java變數執行緒
- 執行緒併發執行緒安全介紹及java.util.concurrent包下類介紹執行緒Java
- Java 併發:執行緒、執行緒池和執行器全面教程Java執行緒
- 多執行緒與高併發(一)多執行緒入門執行緒