package com.bjsxt.base.sync001;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 線程安全概念:當多個線程訪問某一個類(對象或方法)時,這個對象始終都能表現出正確的行爲,那麼這個類(對象或方法)就是線程安全的。
* synchronized:可以在任意對象及方法上加鎖,而加鎖的這段代碼稱爲"互斥區"或"臨界區"
* @author alienware
*
*/
public class MyThread extends Thread{
private int count = 5 ;
//synchronized加鎖
public void run(){
count--;
System.out.println(this.currentThread().getName() + " count = "+ count);
}
public static void main(String[] args) {
/**
* 分析:當多個線程訪問myThread的run方法時,以排隊的方式進行處理(這裏排對是按照CPU分配的先後順序而定的),
* 一個線程想要執行synchronized修飾的方法裏的代碼:
* 1 嘗試獲得鎖
* 2 如果拿到鎖,執行synchronized代碼體內容;拿不到鎖,這個線程就會不斷的嘗試獲得這把鎖,直到拿到爲止,
* 而且是多個線程同時去競爭這把鎖。(也就是會有鎖競爭的問題)
*/
MyThread myThread = new MyThread();
Thread t1 = new Thread(myThread,"t1");
Thread t2 = new Thread(myThread,"t2");
Thread t3 = new Thread(myThread,"t3");
Thread t4 = new Thread(myThread,"t4");
Thread t5 = new Thread(myThread,"t5");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
執行結果:
1線程安全的概念:
當多個線程訪問一個類(對象或者方法)時,這個類始終能表現出正確的行爲,那麼這個類()對象或者方法
就是線程安全的
synchronized 可以在任意對象以及方法上加鎖,餓加鎖的代碼叫‘互斥區’或者‘臨界區
package com.bjsxt.base.sync001;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 線程安全概念:當多個線程訪問某一個類(對象或方法)時,這個對象始終都能表現出正確的行爲,那麼這個類(對象或方法)就是線程安全的。
* synchronized:可以在任意對象及方法上加鎖,而加鎖的這段代碼稱爲"互斥區"或"臨界區"
* @author alienware
*
*/
public class MyThread extends Thread{
private int count = 5 ;
//synchronized加鎖
public synchronized void run(){
count--;
System.out.println(this.currentThread().getName() + " count = "+ count);
}
public static void main(String[] args) {
/**
* 分析:當多個線程訪問myThread的run方法時,以排隊的方式進行處理(這裏排對是按照CPU分配的先後順序而定的),
* 一個線程想要執行synchronized修飾的方法裏的代碼:
* 1 嘗試獲得鎖
* 2 如果拿到鎖,執行synchronized代碼體內容;拿不到鎖,這個線程就會不斷的嘗試獲得這把鎖,直到拿到爲止,
* 而且是多個線程同時去競爭這把鎖。(也就是會有鎖競爭的問題)
*/
MyThread myThread = new MyThread();
Thread t1 = new Thread(myThread,"t1");
Thread t2 = new Thread(myThread,"t2");
Thread t3 = new Thread(myThread,"t3");
Thread t4 = new Thread(myThread,"t4");
Thread t5 = new Thread(myThread,"t5");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
執行結果:
總結:
當多個線程訪問myThread方法時,以排隊的方法進行處理(排隊是按照cpu分配的先後順序而定的,一個線程想要執行synchronized修飾的方法裏的代碼時,首先嚐試獲得鎖,如果拿到鎖,執行synchronized代碼體的內容,拿不到鎖時,這個線程就會不斷的嘗試獲得這把鎖,直到拿到爲止,而且時多個線程同時爭取這把鎖(也就是鎖競爭的問題))
=================================
2多個線程多個鎖:多個線程,每個線程都可以拿到自己指定的鎖,分別獲得鎖後,執行synchronized方法體的內容
package com.bjsxt.base.sync002;
/**
* 關鍵字synchronized取得的鎖都是對象鎖,而不是把一段代碼(方法)當做鎖,
* 所以代碼中哪個線程先執行synchronized關鍵字的方法,哪個線程就持有該方法所屬對象的鎖(Lock),
*
* 在靜態方法上加synchronized關鍵字,表示鎖定.class類,類一級別的鎖(獨佔.class類)。
* @author alienware
*
*/
public class MultiThread {
private int num = 0;
/** static */
public synchronized void printNum(String tag){
try {
if(tag.equals("a")){
num = 100;
System.out.println("tag a, set num over!");
Thread.sleep(1000);
} else {
num = 200;
System.out.println("tag b, set num over!");
}
System.out.println("tag " + tag + ", num = " + num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//注意觀察run方法輸出順序
public static void main(String[] args) {
//倆個不同的對象
final MultiThread m1 = new MultiThread();
final MultiThread m2 = new MultiThread();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
m1.printNum("a");
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
m2.printNum("b");
}
});
t1.start();
t2.start();
}
}
每個對象都有自己的鎖
2.2
同步:synchronized
同步的概念就是共享,如果不是共享的資源,就沒不要進行同步
異步:asynchronized
異步的概念就是獨立,相互之間不受任何影響
同步的目的就是線程安全,對於線程安全來說,需要滿足兩個特性:原子性(同步),可見行
package com.bjsxt.base.sync003;
/**
* 對象鎖的同步和異步問題
* @author alienware
*
*/
public class MyObject {
public synchronized void method1(){
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/** synchronized */
public void method2(){
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
final MyObject mo = new MyObject();
/**
* 分析:
* t1線程先持有object對象的Lock鎖,t2線程可以以異步的方式調用對象中的非synchronized修飾的方法
* t1線程先持有object對象的Lock鎖,t2線程如果在這個時候調用對象中的同步(synchronized)方法則需等待,也就是同步
*/
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
mo.method1();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
mo.method2();
}
},"t2");
t1.start();
t2.start();
}
}
兩個同時執行,因爲第二個方法沒有鎖,兩個互不影響,直接執行
package com.bjsxt.base.sync003;
/**
* 對象鎖的同步和異步問題
* @author alienware
*
*/
public class MyObject {
public synchronized void method1(){
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/** synchronized */
public synchronized void method2(){
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
final MyObject mo = new MyObject();
/**
* 分析:
* t1線程先持有object對象的Lock鎖,t2線程可以以異步的方式調用對象中的非synchronized修飾的方法
* t1線程先持有object對象的Lock鎖,t2線程如果在這個時候調用對象中的同步(synchronized)方法則需等待,也就是同步
*/
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
mo.method1();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
mo.method2();
}
},"t2");
t1.start();
t2.start();
}
}
ti執行完後,過4秒執行t2
兩個方法都加里鎖,一個實例對象執行兩個方法時,都要爭奪鎖,都一個方法的鎖釋放完畢,在執行另一個方法。
示例總結:
a線程先持有object對象的lock鎖,b線程如果在這個時候調用對象中的同步(synchronized)方法需等待,也就是同步
a線程先持有object對象的lock鎖,b線程可以以異步的方式調用對象中的非synchronized修飾得分方法
======================================
3對於對象的同步和異步的方法,設計程序時,一定要考慮問題的整體。不然會出現數據不一致的錯誤,很經典的就是髒讀
package com.bjsxt.base.sync004;
/**
* 業務整體需要使用完整的synchronized,保持業務的原子性。
* @author alienware
*
*/
public class DirtyRead {
private String username = "bjsxt";
private String password = "123";
public synchronized void setValue(String username, String password){
this.username = username;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.password = password;
System.out.println("setValue最終結果:username = " + username + " , password = " + password);
}
public void getValue(){
System.out.println("getValue方法得到:username = " + this.username + " , password = " + this.password);
}
public static void main(String[] args) throws Exception{
final DirtyRead dr = new DirtyRead();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
dr.setValue("z3", "456");
}
});
t1.start();
Thread.sleep(1000);
dr.getValue();
}
}
在setvalue時,不希望getvlaue
package com.bjsxt.base.sync004;
/**
* 業務整體需要使用完整的synchronized,保持業務的原子性。
* @author alienware
*
*/
public class DirtyRead {
private String username = "bjsxt";
private String password = "123";
public synchronized void setValue(String username, String password){
this.username = username;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.password = password;
System.out.println("setValue最終結果:username = " + username + " , password = " + password);
}
public synchronized void getValue(){
System.out.println("getValue方法得到:username = " + this.username + " , password = " + this.password);
}
public static void main(String[] args) throws Exception{
final DirtyRead dr = new DirtyRead();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
dr.setValue("z3", "456");
}
});
t1.start();
Thread.sleep(1000);
dr.getValue();
}
}
示例總結:
在我們對一個對象的方法加鎖時,需要考慮業務的整體行,即setValue/getValue方法 同時加鎖時,保證
業務的原子性,不然會出現業務錯誤。
=============================
synchronized鎖重入:
在使用synchronized時,當一個線程得到了一個對象的鎖後,再次請求此對象時可以再次得到該對象的鎖。
package com.bjsxt.base.sync005;
/**
* synchronized的重入
* @author alienware
*
*/
public class SyncDubbo1 {
public synchronized void method1(){
System.out.println("method1..");
method2();
}
public synchronized void method2(){
System.out.println("method2..");
method3();
}
public synchronized void method3(){
System.out.println("method3..");
}
public static void main(String[] args) {
final SyncDubbo1 sd = new SyncDubbo1();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
sd.method1();
}
});
t1.start();
}
}
package com.bjsxt.base.sync005;
/**
* synchronized的重入
* @author alienware
*
*/
public class SyncDubbo2 {
static class Main {
public int i = 10;
public synchronized void operationSup(){
try {
i--;
System.out.println("Main print i = " + i);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class Sub extends Main {
public synchronized void operationSub(){
try {
while(i > 0) {
i--;
System.out.println("Sub print i = " + i);
Thread.sleep(100);
this.operationSup();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
Sub sub = new Sub();
sub.operationSub();
}
});
t1.start();
}
}
父類和子類都有synchronized修飾時,也是沒有問題的
=========================================================
package com.bjsxt.base.sync005;
/**
* synchronized異常
* @author alienware
*
*/
public class SyncException {
private int i = 0;
public synchronized void operation(){
while(true){
try {
i++;
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + " , i = " + i);
if(i == 10){
Integer.parseInt("a");
}
} catch (Exception e) {//InterruptedException
e.printStackTrace();
//throw new RuntimeException();
System.out.print("記錄日誌");
continue;
}
}
}
public static void main(String[] args) {
final SyncException se = new SyncException();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
se.operation();
}
},"t1");
t1.start();
}
}
package com.bjsxt.base.sync005;
/**
* synchronized異常
* @author alienware
*
*/
public class SyncException {
private int i = 0;
public synchronized void operation(){
while(true){
try {
i++;
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + " , i = " + i);
if(i == 10){
Integer.parseInt("a");
//throw new RuntimeException();
}
} catch (Exception e) {//InterruptedException
e.printStackTrace();
throw new RuntimeException();
}
}
}
public static void main(String[] args) {
final SyncException se = new SyncException();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
se.operation();
}
},"t1");
t1.start();
}
}
package com.bjsxt.base.sync005;
/**
* synchronized異常
* @author alienware
*
*/
public class SyncException {
private int i = 0;
public synchronized void operation(){
while(true){
try {
i++;
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + " , i = " + i);
if(i == 10){
Integer.parseInt("a");
//throw new RuntimeException();
}
} catch (InterruptedException e) {//InterruptedException
e.printStackTrace();
//throw new RuntimeException();
}
}
}
public static void main(String[] args) {
final SyncException se = new SyncException();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
se.operation();
}
},"t1");
t1.start();
}
}
重要:當在i-=10時,拋出一個異常,他還會繼續往下走,把下邊的走完!!!Exception 可以在catch中記錄一下日誌
當整個方法是一個整體,一個失敗,會影響下邊的程序,拋出InterruptedException,他會停止走下邊
當整個方法是一個整體,一個失敗,會影響下邊的程序,在catch中拋出異常:throw new RuntimeException();他會停止走下邊
===========================
volatile:
主要作用:使變量在多個線程間可見
有一個共享變量,a=0,開了兩個線程t1,t2.t1對a進行了修改,t2線程也對a進行了修改。兩個線程直接是互不可見的。
希望a保持一個一致性,t1改成了20,t2也應該改成20.volitile可以起到這個作用。
沒有volatile時,我們用鎖做到了這個效果,保持了a變量的一致性。但是這樣的效率低,因爲同一時間只有一個線程能操作共有的變量,其他線程只能等着
package com.bjsxt.base.sync007;
public class RunThread extends Thread{
private boolean isRunning = true;//private volatile boolean isRunning = true;
private void setRunning(boolean isRunning){
this.isRunning = isRunning;
}
public void run(){
System.out.println("進入run方法..");
int i = 0;
while(isRunning == true){
//..
}
System.out.println("線程停止");
}
public static void main(String[] args) throws InterruptedException {
RunThread rt = new RunThread();
rt.start();
Thread.sleep(1000);
rt.setRunning(false);
System.out.println("isRunning的值已經被設置了false");
}
}
雖然設置成了false,但是程序並沒有停止。
原因:jdk1.5後,對每個線程加了一個運行空間,裝主線程的一些引用變量。直接去父本取
裝了isRunning=true.在變量上加volatile,他就會停止了。
==========================================
package com.bjsxt.base.sync007;
import java.util.concurrent.atomic.AtomicInteger;
/**
* volatile關鍵字不具備synchronized關鍵字的原子性(同步)
* @author alienware
*
*/
public class VolatileNoAtomic extends Thread{
private static volatile int count;
//private static AtomicInteger count = new AtomicInteger(0);
private static void addCount(){
for (int i = 0; i < 1000; i++) {
count++ ;
//count.incrementAndGet();
}
System.out.println(count);
}
public void run(){
addCount();
}
public static void main(String[] args) {
VolatileNoAtomic[] arr = new VolatileNoAtomic[100];
for (int i = 0; i < 10; i++) {
arr[i] = new VolatileNoAtomic();
}
for (int i = 0; i < 10; i++) {
arr[i].start();
}
}
}
volatile關鍵字不具備synchronized關鍵字的原子性(同步)
package com.bjsxt.base.sync007;
import java.util.concurrent.atomic.AtomicInteger;
/**
* volatile關鍵字不具備synchronized關鍵字的原子性(同步)
* @author alienware
*
*/
public class VolatileNoAtomic extends Thread{
//private static volatile int count;
private static AtomicInteger count = new AtomicInteger(0);
private static void addCount(){
for (int i = 0; i < 1000; i++) {
//count++ ;
count.incrementAndGet();
}
System.out.println(count);
}
public void run(){
addCount();
}
public static void main(String[] args) {
VolatileNoAtomic[] arr = new VolatileNoAtomic[100];
for (int i = 0; i < 10; i++) {
arr[i] = new VolatileNoAtomic();
}
for (int i = 0; i < 10; i++) {
arr[i].start();
}
}
}
總結:volatile雖然擁有多個線程之間的可見性,但是不具備同步性(也就是原子性),可以算上一個輕量級的synchronized。性能比synchronized強很多,不會造成阻塞。比如netty的底層代碼就大量使用了volatutle,可見netty性能一定時不錯的,但需要注意的是,一般volatitle用於只針對多個線程可見的變量操作,並不能替代synchronized的同步功能。
volatile只具有可見性,沒有原子性。要實現原子性建議使用atomic類的系列對象,支持原子性操作
(注意 atomic類只保證本身方法原子性沒,並不能保持多次操作的原子性)
=============================