1. 多線程程式設計

2. Thread和Runnable
java中實作多線程的方式有兩種,繼承Thread類、實作Runnable接口
2.1 Thread
開發人員可以編寫一個類繼承Thread,并重寫run方法,在run方法裡面編寫線程将要執行的代碼。
建立線程對象後,隻需要調用start()方法即可讓線程進入就緒隊列,等待作業系統排程。
需要特别注意的是排程具有随機性和随時性。也就是說無法确定下一次排程哪個線程,也無法确定什麼時刻進行排程
public class MyThreadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();//把myThread加入到就緒隊列裡面
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread執行了");
}
}
2.2 Runnable
除了繼承Thread重寫run方法外,在簡單的情況下,還可通過實作Runnable接口的方式編寫線程執行的代碼
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Runnable接口方式實作多線程");
}
});
3.線程安全問題
一個資料,如一個對象或對象中的某個字段,如果有多個線程可以同時通路它,就可能會出現線程安全問題:資料錯亂、程式出錯或其他無法預知的問題
比如線程1要周遊一個list,線程2要把這個list清空,如果這兩個線程同時執行就可能會出現線程安全問題
public class ThreadQuestionTest {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 100000; i++) {
list.add(i);
}
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
list.clear();
}
});
thread1.start();
thread2.start();
}
}
輸出:
0
null
null
null
null
...
4. 線程同步
線程同步控制,即使用某種方式使得一個線程在操作完某個資料前,别的線程無法操作這個資料,進而避免多個線程同時操作一個資料,進而避免線程安全問題
線程同步控制的方式有同步鎖機制、等待/通知機制、信号量機制等,它們應用在不同複雜度的場景下
4.1同步代碼塊
synchronized同步鎖機制
Java中每個對象都有一把鎖,同一時刻隻能有一個線程持有這把鎖。線程可以使用synchronized關鍵字向系統申請某個對象的鎖,得到鎖之後,别的線程再申請該鎖時,就隻能等待。持有鎖的線程在這次操作完成後,可以釋放鎖,以便其他線程可以獲得鎖
synchronized有兩種形式,synchronized代碼塊和synchronized方法
synchronized代碼塊,又稱同步代碼塊:
public class SynchronizedBlockTest {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 100000; i++) {
list.add(i);
}
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (list) {
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (list) {
list.clear();
}
}
});
thread1.start();
thread2.start();
}
}
View Code
4.2 同步方法
public class SynchronizedMethodTest {
public static void main(String[] args) {
Data data = new Data();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
data.bianliList();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
data.clearList();
}
});
thread1.start();
thread2.start();
}
}
class Data {
private List<Integer> list;
public Data() {
list = new ArrayList<Integer>();
for (int i = 0; i < 100000; i++) {
list.add(i);
}
}
public synchronized void bianliList() {
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
public synchronized void clearList() {
list.clear();
}
}
View Code
非靜态同步方法申請的鎖是類的目前對象的鎖,靜态同步方法申請的鎖是類的Class對象的鎖。同步方法執行完後即向系統歸還鎖
synchronized代碼塊和synchronized方法的效果一樣,可根據具體場景靈活選用
對于簡單的需要線程同步控制的應用場景,synchronized基本夠用
但需要注意,所有需要同步的線程必須都申請同一個對象的鎖,當申請不同的鎖或者有的線程沒有使用synchronized時,同步鎖機制就會失效
5. wait/notify 等待/通知機制
對于稍複雜的情況,比如多個線程需要互相合作有規律的通路共享資料,就可以使用wait/notify機制,即等待/通知機制,也稱等待/喚醒機制
等待/通知機制建立在synchronized同步鎖機制的基礎上,即在同步代碼塊(或同步方法)内,如果目前線程執行了lockObject.wait()(lockObject表示提供鎖的對象),則目前線程立即暫停執行,并被放入阻塞隊列,并向系統歸還所持有的鎖,并在lockObject上等待,直到别的線程調用lockObject.notify()
如果有多個線程在同一個對象上等待,notify()方法隻會随機通知一個等待的線程,也可以使用notifyAll()方法通知所有等待的線程。被通知的線程獲得鎖後會進入就緒隊列
public class WaitNotifyTest {
public static void main(String[] args) {
Object lockObject = new Object();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lockObject) {
try {
System.out.println("線程1即将開始在lockObject上等待");
lockObject.wait();
System.out.println("線程1收到通知并獲得鎖,開始繼續執行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lockObject) {
System.out.println("線程2将随機通知在lockObject上等待的線程");
lockObject.notify();
}
}
});
thread2.start();
thread1.start();
}
}
View Code
5.1 wait/notify-生産者消費者執行個體
一個很典型的生産者消費者例子:現有一個生産者、一個消費者、10個盤子(緩沖區),生産者把生産的産品放入空盤子中,當沒有空盤子時就停止生産;消費者消費盤子中的産品,當所有的盤子都是空盤子時就停止消費
public class ProducerConsumerTest{
public static void main(String[] args) {
List<Integer> buffer = new LinkedList<Integer>();
int maxSize = 10;
Producer producer = new Producer(buffer, maxSize);
Consumer consumer = new Consumer(buffer);
producer.start();
consumer.start();
}
}
//模拟生産者
class Producer extends Thread {
private List<Integer> buffer; //緩沖區,表示多個盤子
private int maxSize; //表示盤子個數
public Producer(List<Integer> buffer, int maxSize) {
this.buffer = buffer;
this.maxSize = maxSize;
}
@Override
public void run() {
int id = 0;
while (true) {
synchronized (buffer) {
if (buffer.size() < maxSize) {//有空盤子則繼續生産
id++;//表示生産了一個産品
buffer.add(id); //表示把産品放入一個空盤子中
System.out.println("生産産品" + id + "并通知消費者可以消費了");
buffer.notify(); //通知消費者有産品可以消費了
} else {
//如果沒有空盤子則等待
System.out.println("沒有空盤子了,生産者停止生産産品");
try {
buffer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
//模拟消費者
class Consumer extends Thread {
private List<Integer> buffer; //緩沖區,表示多個盤子
public Consumer(List<Integer> buffer) {
this.buffer = buffer;
}
@Override
public void run() {
while (true) {
synchronized (buffer) {
if (buffer.size() > 0) { //有不空的盤子
int id = buffer.remove(0); //表示消費了一個産品
System.out.println("消費産品" + id + "并通知生産者有空盤了");
buffer.notify();
} else {
//全部都是空盤子則等待
System.out.println("全部都是空盤子,消費者停止消費産品");
try {
buffer.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
View Code
轉載于:https://www.cnblogs.com/renjing/p/thread.html