線程同步:就是在執行代碼的時候,同一個資源,被多個線程訪問,此時協調這多個線程得訪問就是線程同步。
解決比較簡單的一種方法是,實用synchronized 同步鎖,讓下一個線程等待上一個線程執行結束。然後執行,在上synchronized鎖得時候,需要注意,這把鎖的粒度。
在上synchronized鎖得時候經常會碰見的一個問題是,『死鎖』
前提:倆個或以上的線程
線程A,整個操作過程中鎖住了a(先)對象和b(後)對象。
線程B,整個操作過程中鎖住了b(先)對象和a(後)對象。
線程A 將a對象執行完了,執行到b對象的時候,發現b對象已經被線程B鎖定,那麼線程A就在等待中,a的鎖是不會釋放。
線程B執行完b對象的操作的時候,發現a對象已經被A線程給鎖定,於是也進入等待,此時b得鎖也是不會釋放。
於是線程A和線程B都等待對方釋放鎖,於是兩個線程就都執行不下去了
public class Account implements Runnable{
public int flag = 1;
static Object o1 = new Object();
static Object o2 = new Object();
@Override
public void run() {
System.out.println("flag= " + flag);
if (flag == 1){
synchronized (o1){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2){
System.out.println("1");
}
}
}
if (flag==0){
synchronized (o2){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){
System.out.println("0");
}
}
}
}
public static void main(String[] args){
Account t1 = new Account();
Account t2 = new Account();
t1.flag=1;
t2.flag=0;
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
thread1.start();
thread2.start();
}
}
程序會一直在這裏執行,
解決死鎖得方法:
- 最好只鎖住一個,不要鎖住多個,也就是將鎖得粒度加粗一些
- 鎖定當前整個對象就可以了,爲什麼要在這個對象下面鎖定兩個小對象呢?
- 鎖的粒度加粗些,當然還有很多其它的辦法
生產者-消費者模式
package Basic;
public class ProducerConsumer{
public static void main(String[] args){
SyncStack syncStack = new SyncStack();
Producer producer = new Producer(syncStack);
Consumer consumer = new Consumer(syncStack);
new Thread(producer).start();
new Thread(consumer).start();
}
}
/*
饅頭類
id: 每一個饅頭得id
*/
class WoTou{
int id;
public WoTou(int id) {
this.id = id;
}
@Override
public String toString() {
return "WoTou{" + "id=" + id + '}';
}
}
class SyncStack{
int index = 0;
WoTou[] arrWT = new WoTou[6];
/*
裝饅頭
*/
public synchronized void push(WoTou woTou){
while (index == arrWT.length){
/*
這裏主要的是 wait 這個等待方法,執行這個方法的前提是 synchronized 將這個對象鎖住
鎖住之後才能去 wait 等待,等待過程中會釋放鎖
當等待之後,這個對象的鎖就不在歸我管理了,只有當我醒來的時候,採取找這把鎖
當一個線程來給我塞饅頭得時候,如果這個時候,饅頭已經塞滿了,不能再往裏面塞了
那麼就要去阻塞住,
等待消費者將饅頭買完,才能繼續生產(塞饅頭)
*/
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/*
wait 和 notify 得方法是一致的,
notify 方法叫醒一個正在wait在我這個對象上的方法,讓他繼續執行
notifyAll 方法叫醒所有正在wait在我這個對象上的方法,讓他繼續執行
*/
this.notify();
arrWT[index] = woTou;
index ++;
}
/*
吃饅頭
*/
public synchronized WoTou pop(){
while (index==0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notify();
index --;
return arrWT[index];
}
}
/*
生產者一次生產 20 個饅頭
*/
class Producer implements Runnable{
SyncStack ss = null;
public Producer(SyncStack ss) {
this.ss = ss;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
WoTou woTou = new WoTou(i);
ss.push(woTou);
System.out.println("生產了:" + woTou);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/*
消費者一次吃 20 個饅頭
*/
class Consumer implements Runnable{
SyncStack ss = null;
public Consumer(SyncStack ss) {
this.ss = ss;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
WoTou pop = ss.pop();
System.out.println("消費了:" + pop);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}