線程安全的概念:
當多個線程訪問某個類(對象或者方法)時,這個類始終都能表現出正確的行爲,那麼這個類就是線程安全
synchronized:可以在任意對象以及方法上加鎖,而加鎖的這段代碼稱爲互斥去或者臨界區
package mythread;
/**
* 線程安全概念:當多個線程訪問某一個類(對象或方法)時,這個對象始終都能表現出正確的行爲,那麼這個類(對象或方法)就是線程安全的。
* 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();
}
}
-----> 當多個線程訪問my Thread的run方法時,以排隊的方法進行處理,這裏排隊是按照CPU分配的先後順序而定的,一個線程想要執行synchronized修飾的方法裏的代碼,首先是嘗試獲得鎖,如果拿到鎖。,執行synchronized代碼體內容,拿不到鎖,這個線程就會不斷的嘗試獲得這把鎖,直到拿到爲止,而且是多個線程同時去競爭這把鎖。
多個線程多個鎖
package mythread;
/**
* 關鍵字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();
}
}
對象鎖的同步和異步
同步:syschronized
同步的慨念就是共享,我們要牢牢記住共享着兩個字,如果不是共享的資源就沒必要進行工程
異步:asychronized
異步的慨念就是獨立,互相之前不收到任何制約,就好像我們學習http的時候,在頁面發起的Ajax請求,我們還可以繼續瀏覽或操作頁面的內容,二者之間沒有任何關係
同步的目的就是爲了線程安全,其實對於線程安全來說,需要滿足兩個特性
原子性
可見性
package mythread;
/**
* 對象鎖的同步和異步問題
* @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 mythread;
/**
* 業務整體需要使用完整的synchronized,保持業務的原子性。
* @author alienware
*
*/
public class DirtyRead {
private String username = "wkn";
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);
}
//這個時候如果在設置的時候有線程調用這個方法會出現藏獨的問題,解決的方法時加synchronized
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 t2 = new Thread(new Runnable() {
// @Override
// public void run() {
// dr.getValue();
// }
// });
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
dr.setValue("z3", "456");
}
});
t1.start();
Thread.sleep(1000);
dr.getValue();
}
}
syschronized其他概念
package sync005;
/**
* synchronized的重入
* @author wkn
*
*/
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 sync005;
/**
* synchronized的重入
* @author wkn
*
*/
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();
}
}
package com.bjsxt.base.sync006;
/**
* 使用synchronized代碼塊加鎖,比較靈活
* @author alienware
*
*/
public class ObjectLock {
public void method1(){
synchronized (this) { //對象鎖
try {
System.out.println("do method1..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void method2(){ //類鎖
synchronized (ObjectLock.class) {
try {
System.out.println("do method2..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private Object lock = new Object();
public void method3(){ //任何對象鎖
synchronized (lock) {
try {
System.out.println("do method3..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
final ObjectLock objLock = new ObjectLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method1();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method2();
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method3();
}
});
t1.start();
t2.start();
t3.start();
}
}
//不要將字符串常量作爲鎖,因爲string在線程池中的引用只有一個,會造成死循環
package sync006;
/**
* synchronized代碼塊對字符串的鎖,注意String常量池的緩存功能
* @author wkn
*
*/
public class StringLock {
public void method() {
//new String("字符串常量")
synchronized ("字符串常量") {
try {
while(true){
System.out.println("當前線程 : " + Thread.currentThread().getName() + "開始");
Thread.sleep(1000);
System.out.println("當前線程 : " + Thread.currentThread().getName() + "結束");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
final StringLock stringLock = new StringLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
},"t2");
t1.start();
t2.start();
}
}