1. 程式人生 > >【JavaSE:1.多執行緒】

【JavaSE:1.多執行緒】

  1. 最初場景:生產者消費者模式
    模擬買票Ticket
package multiThread;

public class Ticket implements Runnable {
//共100票
	int ticket = 100;

	@Override
	public void run() {
//模擬賣票
		while (true) {
			if (ticket > 0) {
//模擬選坐的操作
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
System.out.println(Thread.currentThread().getName() + "正在賣票:" + ticket--); } } } }

測試類ThreadDemo

package multiThread;

public class ThreadDemo {
	public static void main(String[] args) {
//建立票物件
		Ticket ticket = new Ticket();
//建立3個視窗s
		Thread t1 = new Thread(ticket, "視窗1");
		Thread t2 = new
Thread(ticket, "視窗2"); Thread t3 = new Thread(ticket, "視窗3"); t1.start(); t2.start(); t3.start(); } }

===進階版本

package multiThread;

import java.util.LinkedList;
import java.util.Queue;

/**
 * 
 * @author ziboris
 * @date 2018年11月23日 上午9:15:43
 *
 */

//1.queue的所有操作都會notifyAll 但是目前一定有一個執行緒觸發這個notifyall
//其他執行緒因為還需要甦醒之後排程時間,肯定競爭不過他 //因此基本{產生滿,同一個消費者消費完畢},{},{}.... //---》解決方案 在A出加上sleep程式碼塊 public class ProducerConsumer { public static class Producer extends Thread {// 生產者阻塞時候是判滿 Queue<Integer> queue; int maxSize; public Producer(Queue<Integer> queue, int maxSize, String name) { this.queue = queue; this.maxSize = maxSize; this.setName(name); // TODO Auto-generated constructor stub } @Override public void run() { // TODO Auto-generated method stub // LinkedBlockingQueue<Integer> bQueue=new LinkedBlockingQueue<>(); while (true) { // A try { Thread.sleep(500L); } catch (InterruptedException e) { e.printStackTrace(); // TODO: handle exception } synchronized (queue) { System.out.println(this.getName() + " is in lock"); if (queue.size() == maxSize) { System.out.println("queue is full--" + this.getName() + "--so waiting"); try { queue.wait();// wait在queue這個資源上面 儲存一份阻塞時候的狀態,到時候被喚醒的時候可以直接往下面執行 } catch (Exception e) { // TODO: handle exception } } int num = (int) (Math.random() * 100); queue.offer(num);// offer在加滿的時候會返回一個false put會一直等待知道出現空位 add會丟擲異常 System.out.println(this.getName() + "-produce a ticket:-" + num);// 執行緒的子類可以用getname獲取到初始化的name或者setname queue.notifyAll();// 因為其他被阻塞的執行緒都等待在queue這個資源上面 System.out.println(" log out a produce"); } } } } public static class Consumer extends Thread { Queue<Integer> queue; int maxSize; public Consumer(Queue<Integer> queue, int maxSize, String name) { // TODO Auto-generated constructor stub this.queue = queue; this.maxSize = maxSize; this.setName(name); } @Override public void run() { // TODO Auto-generated method stub // super.run(); while (true) { try { Thread.sleep(200L); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } synchronized (queue) { System.out.println(this.getName() + " is in lock"); if (queue.isEmpty()) { System.out.println("queue is empty--" + this.getName() + "--so waiting"); try { queue.wait(); } catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } } int num = queue.poll();// queue的remove和poll都是去除第一個元素,空的時候一個是返回exception,null System.out.println(this.getName() + "- consume a ticket-" + num); queue.notifyAll(); System.out.println(this.getName() + " log out a consume"); } } } } public static void main(String[] args) { Queue<Integer> queue = new LinkedList<>(); int maxSize = 10; Producer producer = new Producer(queue, maxSize, "Producer"); Consumer consumer1 = new Consumer(queue, maxSize, "consumer1"); Consumer consumer2 = new Consumer(queue, maxSize, "consumer2"); Consumer consumer3 = new Consumer(queue, maxSize, "consumer3"); producer.start(); consumer1.start(); consumer2.start(); consumer3.start(); } }
  1. 併發和並行
並行 多個cpu例項或者多臺機器同時執行一段處理邏輯,是真正的同時
併發 通過cpu排程演算法,讓使用者看上去同時執行,實際上從cpu操作層面不是真正的同時。
  1. 執行緒狀態與狀態轉換
    執行緒狀態轉換

  2. join
    下面程式如果不join:
    —Main Thread is finished
    Thread2 begins:Thu Nov 22 20:17:39 CST 2018
    Thread1 begins:Thu Nov 22 20:17:39 CST 2018
    Thread1 has finished: Thu Nov 22 20:17:44 CST 2018
    Thread2 has finished: Thu Nov 22 20:17:44 CST 2018
    發現main結束的時候兩個子執行緒還沒有啟動好
    加上join(1,2先後隨機)
    Thread2 begins:Thu Nov 22 20:20:02 CST 2018
    Thread1 begins:Thu Nov 22 20:20:02 CST 2018
    Thread2 has finished: Thu Nov 22 20:20:07 CST 2018
    Thread1 has finished: Thu Nov 22 20:20:07 CST 2018
    —Main Thread is finished

package multiThread;

import java.util.Date;
import java.util.concurrent.TimeUnit;

public class TestJoin implements Runnable {
	private String name;

	public TestJoin(String name) {
		this.name = name;
	}

	public void run() {
		System.out.printf("%s begins:%s\n", name, new Date());
		try {
			TimeUnit.SECONDS.sleep(5L);
		} catch (InterruptedException e) {
			e.printStackTrace();
			// TODO: handle exception
		}
		System.out.printf("%s has finished: %s\n", name, new Date());
	}

	public static void main(String[] args) {
		Thread thread1 = new Thread(new TestJoin("Thread1"));
		Thread thread2 = new Thread(new TestJoin("Thread2"));
		thread1.start();
		thread2.start();

//		try {
//			thread1.join();
//			thread2.join();
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//			// TODO: handle exception
//		}

		System.out.println("---Main Thread is finished");
	}
}
  1. wait notify && notifyall

Causes the current thread to wait until another thread invokes
沉睡自己,知道其他執行緒喚醒自己 notify notifyall

執行緒在執行的時候,如果發現某些條件沒有被滿足,可以呼叫wait方法暫停自己的執行,並且放棄已經獲得的鎖,然後進入等待狀態。當該執行緒被其他執行緒喚醒並獲得鎖後,可以沿著之前暫停的地方繼續向後執行,而不是再次從同步程式碼塊開始的地方開始執行。但是需要注意的一點是,對執行緒等待的條件的判斷要使用while而不是if來進行判斷。這樣線上程被喚醒後,會再次判斷條件是否正真滿足。

  1. 基本執行緒類 Thread類,Runnable介面,Callable介面
    extends Thread還是implement runnable (二者基本類似 ,區別見下)
    參考博文:java建立執行緒implement runnable 和 extends thread 比較
    Thread(runnable):
//當前執行緒可轉讓cpu控制權,讓別的就緒狀態執行緒執行(切換)
public static Thread.yield()  
//yield()的作用是讓步。它能讓當前執行緒由“執行狀態”進入到“就緒狀態”,大家再次同一起跑線競爭
//暫停一段時間
public static Thread.sleep()  
//在一個執行緒中呼叫other.join(),將等待other執行完後才繼續本執行緒。    
public join()
//後兩個函式皆可以被打斷
public interrupte()
  1. synchronized wait notify notifyAll 是任何物件都具有的方法,因為任何物件都是一個資源

java中每一個物件都有一個monitor監視器,只在多執行緒的時候發揮作用
synchronized wait notify 必須是針對同一個物件資源
每一個物件的monitor有一個進入佇列和等待佇列(實質上多等待著佔有這個資源)

  1. volatile 多執行緒之間實時看見這個變數
    執行緒的建立的時候是將main執行緒記憶體load到自己的執行緒棧裡面,完成run之後再save回去

volatile的作用是每一次的改變操作都會立馬save回去 因此main中需要交給各個執行緒處理的變數要麼是volatile 要麼是final

  1. 高階多線層控制工具類:java.util.concurrent包裡面since jdk1.5

ThreadLocal 為變數在每個執行緒中都建立了一個副本,那麼每個執行緒可以訪問自己內部的副本變數(如session)(牆裂推薦這個連結)
Atomic***(AtomicInteger AtomicBoolean)
Lock
容器類
管理類

【補充】

  1. 執行緒優先順序:

Java執行緒可以有優先順序的設定,高優先順序的執行緒比低優先順序的執行緒有更高的機率得到執行
優先順序可以用從1到10的範圍指定。10表示最高優先順序,1表示最低優先順序,5是普通優先順序(default)

  1. 設定執行緒名字和執行緒組名字
    看執行緒出問題 jstack -pid 使用trycatch不行

執行緒組,批量管理多個執行緒

  1. Thread.currentThread()與this的區別
    參考資料:Thread.currentThread()與this的區別
    this 在類裡面如論如何都指向這個類
    但是在這個自定義執行緒類中,只有run裡面this和currentThread才相等