標籤:zed report 線程同步 finish try 資料 實現 對象 mic
多線程:
一、同步的方法
1、synchronized
合理利用資源,提高效率最有效方法。帶來這些有利的一面的同時,也為開發人員帶來了一些煩擾,如資料不一致,會導致嚴重的後果,目前使用最多的就是通過synchronized來實現資料的同步,從以下幾個方面介紹synchronized:
要解決多線程並發的問題,就是通過排隊的方式,一個一個來,如果方法或代碼塊加上了synchronized,就等於擷取了鎖,其他線程只能等待方法或代碼塊被執行後鎖被釋放,等待的線程擷取到鎖,才能執行方法或代碼塊裡面的代碼。
與synchronized密不可分的是鎖,鎖有類鎖和對象鎖,對象鎖與類鎖各自獨立,互不干擾,會出現下面幾種情況
1.1、在static上面的synchronized可以擷取到類鎖,當有一個方法擷取到類鎖時,其他加了static synchronized的方法必須要等待類鎖被釋放才能擷取到類鎖,並執行裡面的方法
private static synchronized void doA() throws Exception {
Thread.sleep(1000 * 3);// 休眠3秒
System.out.println("A sleep finish");
}
private static synchronized void doB() {
System.out.println("B sleep finish");
}
private static void report(String name) {
System.out.println(name + " start sleep");
}
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
public void run() {
try {
report("A");
doA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
report("B");
doB();
}
}).start();
}
結果:
A start sleep
B start sleep
A sleep finish
B sleep finish
1.2、擷取這個對象鎖,其他方法或代碼塊必須要等到這個對象鎖釋放後才能擷取這個對象鎖,執行後面的代碼。
1.3、擷取同一個對象鎖時,不同的線程需要等待,但不同對象擷取不同的對象鎖時,互不影響
final Object p1 = new Object();
final Object p2 = new Object();
new Thread(new Runnable() {
public void run() {
synchronized (p1) {
System.out.println(Thread.currentThread().getName() + "擷取P1對象鎖");
try {
Thread.sleep(1000 * 3);
System.out.println(Thread.currentThread().getName() + "釋放P1對象鎖");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
public void run() {
synchronized (p1) {
System.out.println(Thread.currentThread().getName() + "擷取P1對象鎖");
}
}
}).start();
輸出:
Thread-0擷取P1對象鎖
Thread-0釋放P1對象鎖
Thread-1擷取P1對象鎖
將下面的線程中的p1改成p2時,輸出如下
Thread-0擷取P1對象鎖
Thread-1擷取P2對象鎖
Thread-0釋放P1對象鎖
2.原子變數
髒讀:資料不一致
volatile關鍵字:新的線程會拷貝一份副本到自己的記憶體中,修改的與主線程公用的變數並不會改變主線程中的變數的值,只有在變數前面加上volatile關鍵字,迫使線程去主線程中的記憶體中擷取或修改變數的值,可以保證變數的可見度,但它並不具備原子性,也是不安全的
同步方法
2.1.加鎖
2.2.使用原子變數
代碼:
public volatile static int volatileCount = 0;
public static int syncCount = 0;
public static AtomicInteger autoCount = new AtomicInteger(0);
public static void inc() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
volatileCount++;
syncInc();
autoInc();
}
private static void autoInc() {
autoCount.incrementAndGet();
}
private static synchronized void syncInc() {
syncCount++;
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 50; i++) {
Thread t = new Thread(new Runnable() {
public void run() {
VolatileTest.inc();
}
});
t.start();
}
Thread.sleep(1000);
System.out.println("運行結果:Counter.volatileCount=" + VolatileTest.volatileCount);
System.out.println("運行結果:Counter.syncCount=" + VolatileTest.syncCount);
System.out.println("運行結果:Counter.autoCount=" + VolatileTest.autoCount);
}
輸出結果:
運行結果:Counter.volatileCount=45
運行結果:Counter.syncCount=50
運行結果:Counter.autoCount=50
3.ThreadLocal
ThreadLocal為並發提供另一種解決方案,通過空間換取時間,為每一個線程提供一個單獨空間
代碼:
public class ThreadLocalTest {
private static ThreadLocal<String> threadLocal = new ThreadLocal<String>();
public static void setValue(String value) {
threadLocal.set(value);
}
public static String getValue() {
return threadLocal.get();
}
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
setValue("fred");
System.out.println("threadLocal=" + getValue());
}
}, "t1").start();
new Thread(new Runnable() {
public void run() {
System.out.println("threadLocal=" + getValue());
}
}, "t2").start();
}
}
結果:
threadLocal=fred
threadLocal=null
二、線程之間的通訊
wait/notify
1、必須用同步關鍵字
2、wait釋放鎖 notify不釋放鎖,notify後不會馬上執行wait線程的代碼,只有notify所線上程執行完後,才會執行wait線程的代碼
代碼(類比阻塞隊列):
public final static LinkedList<String> quene = new LinkedList<String>();
public final static Object lock = new Object();
public final static int QUENE_SIZE = 5;
public final static int QUENE_MIN = 0;
public final static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] arg) {
final MyQueue quene = new MyQueue();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
quene.put(count.getAndIncrement() + "");
}
}).start();
new Thread(new Runnable() {
public void run() {
quene.get();
}
}).start();
}
}
public int getSize() {
return quene.size();
}
public String get() {
synchronized (lock) {
while (QUENE_MIN == getSize()) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String value = quene.get(QUENE_MIN);
quene.remove(QUENE_MIN);
System.out.println("移除" + value);
lock.notify();
return value;
}
}
public void put(String value) {
synchronized (lock) {
while (QUENE_SIZE == getSize()) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
quene.add(getSize(), value);
System.out.println("增加" + value);
lock.notify();
}
}
結果:
增加1
移除1
增加0
增加2
增加3
移除0
移除2
移除3
增加5
增加6
增加7
增加9
移除5
移除6
增加8
移除7
移除9
移除8
增加4
移除4
多線程同步與通訊