Java基礎系列八之線程

JVM:Java虛擬機 識別main(主線程)
  面試題:
   JVM是多線程程序嗎?至少有幾條線程..
   jvm是多線程的,
   至少有2條線程...
   有主線程,main..執行這些代碼,能夠被Jvm識別
       垃圾回收線程
   在執行程序的時候,一些對象會被Jvm釋放掉,原因,

  它開啓了垃圾回收線程,裏面GC:垃圾回收器(回收一些沒有更多引用的對象或者變量...)

代碼示例

public class Demo {

	public static void main(String[] args) {

		System.out.println("hello");

		new Object();
		new Object();
		new Object();
		new Object();
		new Object();
		// ......n個
		System.out.println("world");

	}
}

如何實現多線程程序呢?
 要實現多線程程序,需要開啓進程,
 開啓進程,是需要創建系統資源,但是Java語言不能創建系統資源
 只有C/C++可以創建系統資源, 利用c語言創建好的系統資源實現
 Java提供了一個類:Thread類
  實現多線程程序的步驟:
  1)將該類聲明爲Thread的子類
  2)該子類應重寫 Thread類的 run方法
  3)在主線程創建該自定義線程類的對象
 並行和併發(高併發:MyBatis --->IBatis:半自動化)
 前者邏輯上的同時,指的是同一個時間段內
 後者物理上的同時,指的是同一個時間點

 代碼示例

public class ThreadDemo {

	public static void main(String[] args) {

		// 創建MyThread類對象
		// MyThread my = new MyThread() ;

		// 當前Thread類有一個run public void run()
		// my.run();
		// System.out.println("-------------------");
		// my.run();
		// 執行線程不是run方法 ,run方法的調用相當於一個普通方法的調用

		/*
		 * public void start()使該線程開始執行;Java虛擬機調用該線程的 run 方法。 結果是兩個線程併發地運行
		 */

		MyThread t1 = new MyThread();
		t1.start();
		// IllegalThreadStateException:非法狀態異常,同一個線程只能被執行一次
		// t1.start();
		MyThread t2 = new MyThread();
		t2.start();

	}
}
public final void setDaemon(boolean on) :true時,表示爲守護線程
  將該線程標記爲守護線程或用戶線程。當正在運行的線程都是守護線程時,Java 虛擬機退出。(守護線程不會立即結束掉,它會執行一段時間在結束掉)
  該方法必須在啓動線程前調用。

代碼示例

package thread;

public class ThreadDemo2 {
	public static void main(String[] args) {
		MyThread myThread = new MyThread();
		MyThread myThread2 = new MyThread();
		myThread.setName("張飛");
		myThread2.setName("關羽");
		myThread.setDaemon(true);// 設置爲守護線程
		myThread2.setDaemon(true);// 設置爲守護線程
		myThread.start();
		myThread2.start();
		Thread.currentThread().setName("劉備");
		for (int x = 0; x < 5; x++)
			System.out.println(Thread.currentThread().getName());
	}
}
跟線程的優先級相關的方法:
  public final int getPriority()返回線程的優先級。 
  public final void setPriority(int newPriority)更改線程的優先級
  線程存在一個默認優先級
 
 public static final int MAX_PRIORITY 10 最大優先級
 public static final int MIN_PRIORITY 1 最小優先級
 public static final int NORM_PRIORITY 5 默認優先級

代碼示例

package thread;

public class ThreadDemo3 {
	public static void main(String[] args) {
		MyThread myThread = new MyThread();
		MyThread myThread2 = new MyThread();
		MyThread myThread3 = new MyThread();
		myThread.setName("林青霞");
		myThread2.setName("王祖賢");
		myThread3.setName("林志穎");
		myThread.setPriority(1);
		myThread2.setPriority(5);
		myThread3.setPriority(10);
		System.out.println(myThread3.getPriority());
		myThread2.start();
		myThread.start();
		myThread3.start();
	}
}
public final void join():等待該線程終止 interruputedException 中斷異常

 分別創建三個子線程,讓第一個子線程執行之後,調用join()等待該線程終止,再執行t2,t3線程

代碼示例

package thread;

public class ThreadDemo4 {
	public static void main(String[] args) {
		MyThread myThread = new MyThread();
		MyThread myThread2 = new MyThread();
		MyThread myThread3 = new MyThread();
		myThread.setName("李淵");
		myThread2.setName("李世民");
		myThread3.setName("李元霸");
		myThread.start();
		try {
			myThread.join();
		} catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
		myThread2.start();
		myThread3.start();
	}
}

睡眠

Thread.sleep(long millis)Thread.sleep(long millis, int nanos)靜態方法強制當前正在執行的線程休眠(暫停執行),以減慢線程。當線程睡眠時,它

入睡在某個地方,在甦醒之前不會返回到可運行狀態。當睡眠時間到期,則返回到可運行狀態。

線程睡眠的原因:線程執行太快,或者需要強制進入下一輪,因爲Java規範不保證合理的輪換。 

睡眠的實現:調用靜態方法。

        try {
            Thread.sleep(123);
        } catch (InterruptedException e) {
            e.printStackTrace();  
        }

 

睡眠的位置:爲了讓其他線程有機會執行,可以將Thread.sleep()的調用放線程run()之內。這樣才能保證該線程執行過程中會睡眠。

例如,在前面的例子中,將一個耗時的操作改爲睡眠,以減慢線程的執行。可以這麼寫:

    public void run() {
        for(int i = 0;i<5;i++){

// 很耗時的操作,用來減慢線程的執行
//            for(long k= 0; k <100000000;k++);
            try {
                Thread.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();  .
            }

            System.out.println(this.getName()+" :"+i);
        }
    }

public static void sleep(long millis):線程睡眠 指定是時間毫秒值

   throws InterruptedException 
兩個區別? public final void stop() ;強迫線程停止執行。 不會執行了 (過時了),方法能使用的
   public void interrupt()中斷線程。 表示中斷線程的一種狀態 面試題 區別? wait(): wait()調用的,立即釋放鎖 (同步鎖/Lock鎖)

   sleep(): 線程睡眠,調用不會釋放鎖

代碼示例

package thread;

import java.util.Date;

public class MyThread extends Thread {
	public MyThread() {
		super();
		// TODO Auto-generated constructor stub
	}

	public MyThread(String name) {
		super(name);
		// TODO Auto-generated constructor stub
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		// super.run();
		for (int x = 0; x < 50; x++) {
			try {
				Thread.sleep(1000);
				System.out.println(getName() + "的線程睡眠1毫秒");
			} catch (InterruptedException e) {
				// TODO: handle exception
				e.printStackTrace();
			}
			System.out.println(getName() + ":" + x);
		}
	}

}
package thread;

public class ThreadDemo4 {
	public static void main(String[] args) {
		MyThread myThread = new MyThread();
		MyThread myThread2 = new MyThread();
		myThread.setName("李淵");
		myThread2.setName("李世民");
		myThread.start();
		myThread2.start();
	}
}
需求:某電影院出售某些電影票(復聯3,紅高粱....),有三個窗口同時進行售票(100張票),請您設計一個程序,模擬電影院售票
  兩種方式:
  繼承

  接口

第一種方式:繼承類Thread

代碼示例

package org.westos_01;

//SellTicket線程
public class SellTicket extends Thread {

	// 爲了不讓外界更改這個類中的數據
	// private
	// 定一個票數

	// 爲了讓每一個線程共同使用一個數據,此數據應該被static修飾
	// private int tickets = 100 ;
	private static int tickets = 100;

	@Override
	public void run() {
		// 爲了模擬電影賣票(模擬一直有票)
		// 死循環

		// st1,st2,st3都有執行這個裏面的方法
		// st1 100
		// st2 100
		// st3 100
		while (true) {
			if (tickets > 0) {
				System.out.println(getName() + "正在出售第" + (tickets--) + "張票");
			}
		}
	}
}
public class SellTicketDemo {
	
	public static void main(String[] args) {
		
		//創建三個子線程,分別代碼三個窗口
		SellTicket st1 = new SellTicket() ;
		SellTicket st2 = new SellTicket() ;
		SellTicket st3 = new SellTicket() ;
		
		//設置線程名稱
		st1.setName("窗口1");
		st2.setName("窗口2");
		st3.setName("窗口3");
		
		//啓動線程
		st1.start();
		st2.start();
		st3.start();
	}
}

第二種方式  實現接口Runnable

代碼示例

package org.westos_02;

public class SellTicket implements Runnable {

	// 定義100張票
	private int tickets = 100;

	@Override
	public void run() {

		while (true) {
			if (tickets > 0) {
				System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票");
			}
		}
	}

}
package org.westos_02;

//第二種方式實現

//給繼續加一些內容(延遲操作實現)
public class SellTicketDemo {

	public static void main(String[] args) {

		// 創建資源類對象(共享資源類對象/目標對象(target))
		SellTicket st = new SellTicket();
		// 創建線程類對象
		Thread t1 = new Thread(st, "窗口1");
		Thread t2 = new Thread(st, "窗口2");
		Thread t3 = new Thread(st, "窗口3");

		// 啓動線程
		t1.start();
		t2.start();
		t3.start();
	}
}

 爲了模擬更真實的場景,加入延遲操作(讓我們線程睡100毫秒)
  程序的設計是好的,但是結果有一些問題
  1)同一張票被賣了多次
   CPU的執行有一個特點(具有原子性操作:最簡單最基本的操作)
   2)出現了0或者負票
  (延遲操作+線程執行的隨機性)

代碼示例

package org.westos_03;

public class SellTicket implements Runnable {
	
	//定義100張票
	private int tickets = 100 ;
/*	@Override
	public void run() {
		
		while(true) {
			
			//t1先進來  t2
			if(tickets>0) {
				//爲了模擬更真實的場景(網絡售票有延遲的),稍作休息
				try {
					//t1睡 t2睡
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()
						+"正在出售第"+(tickets--)+"張票");
				//100張票
				//爲什麼會出現同一張票被賣多次?
				*//**
				 * 出現同票的原因:CPU的執行有一個特點(具有原子性操作:最簡單最基本的操作)
				 * t1線程進來,睡完了,100張票
				 * 原子性操作:記錄以前的值
				 * 接着tickets-- :票變成99張票
				 * 在馬上輸出第99張票的時候,t2/t3進來,直接輸出所記錄的之前的票數
				 * 出現:
				 * 	窗口1正在出售第100張票
				 * 	窗口3正在出售第99張票
				 * 	窗口2正在出售第99張票
				 *  原子性操作
			}
		}
	}*/
	
	
	@Override
	public void run() {

		while (true) {
			try {
				// t1睡 t2睡
				Thread.sleep(100); // 延遲操作
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			if (tickets > 0) {
				//t1,t2,t3 三個線程執行run裏面代碼
				//爲了模擬更真實的場景(網絡售票有延遲的),稍作休息
				
				System.out.println(Thread.currentThread().getName()
						+"正在出售第"+(tickets--)+"張票");//0
				/**
				 * 理想狀態:
				 * 		t1正在出售第3張票
				 * 		t3正在出售第2張票
				 * 		t2正在出售第1張票
				 * ...
				 * 		負票
				 * 		t1出售第0張票		(延遲操作+線程執行的隨機性)
				 * 		t3正在出售-1張票
				 * 
				 */
			}
		}
	}

}
public class SellTicketDemo {

	public static void main(String[] args) {

		// 創建資源類對象(共享資源類/目標對象)
		SellTicket st = new SellTicket();

		// 創建線程類對象
		Thread t1 = new Thread(st, "窗口1");
		Thread t2 = new Thread(st, "窗口2");
		Thread t3 = new Thread(st, "窗口3");

		// 啓動線程
		t1.start();
		t2.start();
		t3.start();
	}
}
通過剛纔的這個程序,有安全問題(同票還有負票)
  如何解決多線程的安全問題?
   校驗一個多線程程序是否有安全隱患的前提條件:
   1)當前程序是多線程環境
   2)有共享數據
   3)有多條語句對共享數據進行操作
  看當前案例是否有多線程的安全問題:
   1)是否是多線程環境
   2)是否有共享數據
   3)是否有多條語句對共享數據進行操作  是
  現在就需要解決安全問題:
   1)多線程環境 不能解決
   2)對共享數據進行優化 不能解決
   3)在多條語句對共享數據進行操作這一環進行解決
  
   解決方案:就是將多條語句對共享數據操作的代碼,用同步代碼塊包起來
   格式:
   synchronized(鎖對象){
   針對多條語句對共享數據操作代碼;
  }
  鎖對象:肯定一個對象,隨便創建一個對象(匿名對象) 
   給剛纔的這個程序加入了同步代碼塊,但是鎖對象使用的是匿名對象(每一個線程進來都有自己各自的鎖),還是沒有解決!

   鎖對象:所有線程公用一把鎖

代碼示例

package demo;

public class SellTicket implements Runnable {
	private int tickets = 100;
	private Object object = new Object();

	@Override
	public void run() {
		while (true) {
			synchronized (object) {
				if (tickets > 0) {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票");
				}
			}
		}
	}
}
package demo;

public class SellTicketDemo {
	public static void main(String[] args) {
		// 創建資源類對象(共享資源類對象/目標對象(target))
		SellTicket sellTicket = new SellTicket();
		// 創建線程類對象
		Thread thread = new Thread(sellTicket, "窗口1");
		Thread thread2 = new Thread(sellTicket, "窗口2");
		Thread thread3 = new Thread(sellTicket, "窗口3");
		// 啓動線程
		thread.start();
		thread2.start();
		thread3.start();
	}
}
舉例:
   火車上上廁所(鎖對象:將它看成是門的開和關) 
   synchronized(鎖對象){
多條語句對共享數據進行操作的代碼;
  
 注意:
  鎖對象:一定要使用同一把鎖(所有線程只能公用一把鎖)

  鎖對象:任何的Java類(引用類型)

代碼示例

package org.westos_05;

public class SellTicket implements Runnable {

	// 定義100張票
	private int tickets = 100;

	private Object obj = new Object();
	private Demo d = new Demo();

	@Override
	public void run() {
		while (true) {

			// t1,t2,t3
			synchronized (d) { // 門的開和關

				// t1進來,門會關掉
				// t2進來,門關掉
				// t3進來,門關掉
				if (tickets > 0) {
					try {
						// 0.1
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票");
					// 窗口1正在出售第100張票
					// 窗口2正在出售第99張票
					// 窗口3正在出售98張票
					// ....

					// 雖然加入延遲操作,但是因爲有synchronized,所以不會出現0票或者負票了
				}
			}

		}
	}

}

class Demo {
}
package org.westos_05;

/**
 * 舉例:
 * 		火車上上廁所(鎖對象:將它看成是門的開和關)
 * 
 * 		synchronized(鎖對象){
 * 				多條語句對共享數據進行操作的代碼;
 * 		}
 * 
 *注意:
 *		鎖對象:一定要使用同一把鎖(所有線程只能公用一把鎖)
 *		鎖對象:任何的Java類(引用類型)
 * 			
 *
 */
public class SellTicketDemo {
	
	public static void main(String[] args) {
		
		//創建資源類對象(共享資源類對象/目標對象)
		SellTicket st = new SellTicket() ;
		
		//創建線程類對象
		Thread t1 = new Thread(st, "窗口1") ;
		Thread t2 = new Thread(st ,"窗口2") ;
		Thread t3 = new Thread(st, "窗口3") ;
		
		//啓動線程
		
		t1.start();
		t2.start();
		t3.start();
	}
}

同步方法

當同步方法爲靜態(static)時

private synchronized static void SellTicket(){}

synchronized(類名.class);

當同步方法爲非靜態時

private synchronized void SellTicker(){}

synchronized(this);

代碼示例

package demo;

public class SellTicket implements Runnable {
	private static int tickets = 100;
	private static int x = 100;
	@Override
	public void run() {
		while (true) {
			if (x % 2 == 0) {
				synchronized (this) {
					if (tickets > 0) {
						try {
							Thread.sleep(10);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票"+" x:"+x--);
					}
				}
			}
				else {
				sellTicket();
			}
		}
	}
	//靜態同步方法
	/*private synchronized static void sellTicket() {
		if (tickets > 0) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票"+" x:"+x--);
		}
	}*/
	//非靜態同步方法
	private synchronized void sellTicket() {
		if (tickets > 0) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票"+" x:"+x--);
		}
	}

}

線程安全的類  StringBuffer

線程安全的集合   

                       單列集合  Vector

                       雙列集合  Hashtable

代碼示例

package org.westos_06;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;

public class ThreadDemo {

	public static void main(String[] args) {

		// 線程安全的類StringBuffer Vector<V> Hashtable<K,V>
		StringBuffer sb = new StringBuffer();
		Vector<String> vector = new Vector<String>();
		Hashtable<String, String> hm = new Hashtable<String, String>();

		// Vector<String>它是線程安全的集合,但是還是不習慣使用這個集合,通過ArrayList集合:線程不安全的類
		List<String> arrayList = new ArrayList<String>(); // 線程不安全的類
		ArrayList<String> arrayList2 = new ArrayList<String>();
		// public static <T> List<T> synchronizedList(List<T> list)
		// 返回指定列表支持的同步(線程安全的)列表
		List list = Collections.synchronizedList(arrayList); // 線程安全的方法
		List list2 = Collections.synchronizedList(arrayList2); // 線程安全的方法
	}
}j

jdk5.0以後,java提供了一個具體的鎖: 接口:Lock

private Lock lock=new ReentrantLock();

lock.lock();//顯示獲取鎖

lock.unlock();//釋放鎖

代碼示例

package org.westos_07;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTicket implements Runnable {
	// 定義票
	private int tickets = 100;
	// Object obj = new Object();
	// Jdk5.0以後,java提供了一個具體的鎖: 接口:Lock
	private Lock lock = new ReentrantLock(); // 顯示獲取鎖的前提,一定要創建Lock接口對象

	@Override
	public void run() {
		while (true) {
			try { // try...finally
				lock.lock(); // 獲取鎖 syncrhonized(obj)
				if (tickets > 0) {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "張票");
				}

			} finally {// 釋放鎖
				if (lock != null) {
					lock.unlock();
				}

			}

		}

	}

}

死鎖

簡單描述就是比如說有兩個鎖,第一個鎖中包含着第二個鎖,第二個鎖中也包含着第一個鎖,第一個鎖執行到其代碼塊中有第二個鎖的時候,它會等第二個鎖執行完畢釋放掉後才能執行此處的代碼,必須等待第二個鎖執行完畢,但是線程搶到CPU主動權後執行第二把鎖對應的代碼塊時,執行到其代碼塊中有第一把鎖的地方,必須等第一把鎖執行完畢釋放掉第一把鎖後纔會執行此處代碼,所以第二把鎖在等待第一把鎖執行完畢,就這樣,第一把鎖在等待第二把鎖,第二把鎖也在等待第一把鎖,互相等待啊!!!,就這樣一直等啊等,等啊等,就一直不執行,一直等着,就變成了死鎖。

代碼示例

package org.westos_08;

public class DieLock extends Thread {

	// 聲明一個成員變量
	private boolean flag;

	public DieLock(boolean flag) {
		this.flag = flag;
	}

	// 重寫run方法
	@Override
	public void run() {
		if (flag) {
			synchronized (MyLock.objA) {
				System.out.println("if ObjA");
				synchronized (MyLock.objB) {
					System.out.println("if objB");
				}
			}
		} else {
			synchronized (MyLock.objB) {
				System.out.println("else objB");
				synchronized (MyLock.objA) {
					System.out.println("else objA");
				}
			}
		}
	}

	/**
	 * 第一種情況: if ObjA else objB
	 * 
	 * 第二種情況 else objB if ObjA
	 * 
	 * 第三種情況: 理想狀態 else objB else objA if ObjA if objB
	 * 
	 * if ObjA if objB else objB else objA
	 * 
	 * 
	 */
}
package org.westos_08;

/**
 * 自定義兩個鎖對象
 *
 */
public class MyLock {

	// 兩個鎖對象分別是objA 和objB
	public static final Object objA = new Object();
	public static final Object objB = new Object();
}
package org.westos_08;

//測試類

//解決了多線程安全問題,但是還是有些問題:
//1)執行效率低
//2)會產生死鎖

//兩個或兩個以上的線程,在執行的過程中出現互相等待的情況,就叫做死鎖!
public class DieLockDemo {

	public static void main(String[] args) {

		// 創建線程了對象
		DieLock dl1 = new DieLock(true);
		DieLock dl2 = new DieLock(false);

		// 啓動線程
		dl1.start();
		dl2.start();
	}

}
分析:
   Student類: 資源類
   SetThread:設置學生的數據(生產者線程)
   GetThread:獲取(輸出)學生數據(消費者線程)
  StudentDemo:測試類 
  需求:SetThread線程給學生對象進行賦值,再通過消費者線程輸出該學生數據,設計這樣一個程序!
   null----0 按照剛纔的思路,發現有一個問題,的數據null---0 
  解決方案:線程死鎖的注意事項:要保證生產者線程和消費者線程是針對同一個對象進行操作的!
   在主線程main中創建一個學生對象,將這個學生對象通過構造方法傳給每一個線程

代碼示例

package org.westos_09;

public class Student {

	String name;
	int age;
}
package org.westos_09;

//生產者線程
public class SetThread implements Runnable {

	private Student student;

	public SetThread(Student s) {
		this.student = s;
	}

	@Override
	public void run() {
		// 設置學生數據
		student.name = "高圓圓";
		student.age = 27;
	}
}
package org.westos_09;

//消費者線程
public class GetThread implements Runnable {
	private Student student;

	public GetThread(Student s) {
		this.student = s;
	}

	@Override
	public void run() {
		// 輸出該學生數據
		System.out.println(student.name + "----" + student.age);
	}
}
public class StudentDemo {

	public static void main(String[] args) {

		// 針對同一個對象進行操作
		Student s = new Student();

		// 創建線程類對象
		SetThread st = new SetThread(s);
		GetThread gt = new GetThread(s);

		// 創建線程了對象
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(gt);

		// 啓動線程
		t1.start();
		t2.start();

	}
}

分析:
   Student類: 資源類
   SetThread:設置學生的數據(生產者線程)
GetThread:獲取(輸出)學生數據(消費者線程)
  StudentDemo:測試類 
  需求:SetThread線程給學生對象進行賦值,在通過消費者線程輸出該學生數據,設計這樣一個程序!
   null----0 按照剛纔的思路,發現有一個問題,的數據null---0 
  解決方案:線程死鎖的注意事項:要保證生產者線程和消費者線程針對同一個對象進行操作的!
   在外部創建一個學生對象,將這個學生對象通過構造方法傳入到各個線程中
  需求:消費者線程,和生產者線程加入循環操作,改進
  又有問題:
   1)同一個人的姓名和年齡出現多次
   2)姓名和年齡不符
  爲什麼?
   1)CPU的一點點時間片,在某一個時間點,足夠它執行很多次
   2)線程具有隨機性
 解決方案:
  1)是否是多線程環境
  2)是否有共享數據            是
  3)是否有多條語句對共享數據進行操作
  同步機制(同步代碼塊/同步方法)

  開發中,使用synchronized(Lock鎖也可以)同步代碼塊將多條語句對共享數據的操作包起來!

代碼示例


package org.westos_10;

//生產者線程
public class SetThread implements Runnable {

	private Student s;
	// 定義一個變量
	private int x = 0;

	public SetThread(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		while (true) {
			synchronized (s) {
				if (x % 2 == 0) {
					s.name = "高圓圓";
					s.age = 27;
				} else {
					s.name = "張楊";
					s.age = 28;
				}
				x++;

			}

		}

	}
}
package org.westos_11;

//消費者線程
public class GetThread implements Runnable {
	private Student s;

	public GetThread(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		// 輸出該學生數據
		while (true) {
			synchronized (s) {
				// 如果本身消費者有數據
				if (!s.flag) { // true==!false s.flag==false 說明沒有數據 !flag
					try {
						s.wait();// 和網絡編程裏面的accept()一樣 都屬於阻塞式方法
						// 消費線程等待,等待生產者線程生產數據

					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				} // !s.flag==false s.flag==true 說明有數據,有數據則輸出數據
				System.out.println(s.name + "----" + s.age);// 高圓圓---27
				// 輸出之後就沒有數據了
				s.flag = flase;
				// 通知生產者線程,讓其趕緊生產數據
				s.notify(); // 喚醒生產者線程
			}
		}

	}
}
package org.westos_11;

//消費者線程
public class GetThread implements Runnable {
	private Student s;

	public GetThread(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		// 輸出該學生數據
		while (true) {
			synchronized (s) {
				// 如果本身消費者有數據
				if (!s.flag) { // true==!false s.flag==false 說明沒有數據 !flag
					try {
						s.wait();// 和網絡編程裏面的accept()一樣 都屬於阻塞式方法
						// 消費線程等待,等待生產者線程生產數據

					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				} // !s.flag==false s.flag==true 說明有數據,有數據則輸出數據
				System.out.println(s.name + "----" + s.age);// 高圓圓---27
				// 輸出之後就沒有數據了
				s.flag = false;
				// 通知生產者線程,讓其趕緊生產數據
				s.notify(); // 喚醒單生產者線程
			}
		}

	}
}
public class StudentDemo {

	public static void main(String[] args) {

		// 針對同一個對象進行操作
		Student s = new Student();
		// 創建線程類對象
		SetThread st = new SetThread(s);
		GetThread gt = new GetThread(s);
		// 創建線程類對象
		Thread t1 = new Thread(st); // 生產者
		Thread t2 = new Thread(gt);// 消費者
		// 啓動線程
		t1.start();
		t2.start();
	}
}

ThreadGroup 線程組

線程組:表示一個線程的集合。此外,線程組也可以包含其他線程組 

代碼示例

package org.westos_12;

/**
 * 線程組:表示一個線程的集合。此外,線程組也可以包含其他線程組
 *
 * 線程池(某個線程執行完畢,反覆利用線程對象)
 *
 */
public class ThreadGroupDemo {

	public static void main(String[] args) {

		// 獲取線程組的名稱
		//
		// method1();

		// 如何給多個線程設置一個線程組名稱呢?
		method2();
	}

	private static void method2() {
		// public ThreadGroup(String name)構造一個新線程組
		ThreadGroup tg = new ThreadGroup("main-新的線程組");

		MyThread my = new MyThread();

		// Thread(ThreadGroup group, Runnable target, String name)
		Thread t1 = new Thread(tg, my, "線程1");
		Thread t2 = new Thread(tg, my, "線程2");

		// 直接獲取線程組名稱
		System.out.println(t1.getThreadGroup().getName());
		System.out.println(t2.getThreadGroup().getName());
	}

	private static void method1() {
		MyThread my = new MyThread();

		// 創建線程類對象

		Thread t1 = new Thread(my, "線程1");
		Thread t2 = new Thread(my, "線程2");

		// public final ThreadGroup getThreadGroup()返回該線程所屬的線程組
		ThreadGroup tg1 = t1.getThreadGroup();
		ThreadGroup tg2 = t2.getThreadGroup();

		// public final String getName():返回線程組的名稱
		System.out.println(tg1.getName()); // main
		System.out.println(tg2.getName());// main

		// 所有的線程它默認的線程組名稱:main(主線程)
		System.out.println(Thread.currentThread().getThreadGroup().getName());// main
	}
}
package org.westos_12;

public class MyThread implements Runnable {
	@Override
	public void run() {
		for (int x = 0; x < 100; x++) {
			System.out.println(Thread.currentThread().getName() + ":" + x);
		}

	}

}

線程池 ExecutorService

線程池:多個線程執行完畢,它會重新回到線程池中,等待被利用,不會變成垃圾!
  和線程池有關的類
 類 Executors: 一種工廠類
  方法:
  和線程池的創建有關係
  public static ExecutorService newFixedThreadPool(int nThreads)
  創建一個可重用固定線程數的線程池
 
 ExecutorService:可以執行異步任務
  創建一個線程池,執行接口中的方法
  提交:Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task)提交一個返回值的任務用於執行,返回一個表示任務的未決結果的 Future
 Future:接口
  Future 表示異步計算的結果
  線程池調用完畢可以關閉的
  void shutdown():關閉之前,會提交剛纔的任務

代碼示例

public class ExceutorsDemo {

	public static void main(String[] args) {

		// 創建一個線程池
		ExecutorService pool = Executors.newFixedThreadPool(2);// 創建一個線程池中包含了2條線程

		// 提交和Runnable接口的方法或者Callable(提交任務)
		pool.submit(new MyRunnable());
		pool.submit(new MyRunnable());

		// pool-1-thread-2 :線程池-池數-線程類對象的描述-編號(從1開始)

		// 關閉線程池
		pool.shutdown();

	}
}
package org.westos_13;

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		for (int x = 0; x < 100; x++) {
			System.out.println(Thread.currentThread().getName() + ":" + x);
		}

	}

}

Callable 

call()方法

多線程第三種實現方式:
  前提:自定義類實現Callable接口
  1)創建線程池對象: Executors 裏面的那個方法,返回的是ExecutorsService
  2) 然後調用ExecutorsService裏面的提交任務的方法:
  <T> Future<T> submit(Callable<T> task)提交一個返回值的任務用於執行
  3)關閉線程池

代碼示例

public class ExecutorsDemo {

	public static void main(String[] args) {

		// 創建線程池對象
		ExecutorService pool = Executors.newFixedThreadPool(2);

		// 提交任務
		pool.submit(new MyCallable());
		pool.submit(new MyCallable());

		// 關閉線程池
		pool.shutdown();
	}

}
package org.westos_14;

import java.util.concurrent.Callable;

//Callable的泛型的類型它是call方法的返回值類型
public class MyCallable implements Callable {

	@Override
	public Object call() throws Exception {
		for (int x = 0; x < 100; x++) {
			System.out.println(Thread.currentThread().getName() + ":" + x);
		}

		return null;
	}

}

需求:利用線程來計算數字之和

代碼示例

package org.westos_15;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 分別計算每個線程的求和!
 *
 */
public class ExecutorsTest {

	public static void main(String[] args) throws InterruptedException, ExecutionException {

		// 創建線程池對象
		ExecutorService pool = Executors.newFixedThreadPool(2);

		// 提交任務
		Future<Integer> f1 = pool.submit(new MyCallable(100));
		Future<Integer> f2 = pool.submit(new MyCallable(200));

		// V get():獲取結果
		Integer i1 = f1.get();
		Integer i2 = f2.get();

		System.out.println(i1);
		System.out.println(i2);

		// 關閉線程池
		pool.shutdown();
	}
}
package org.westos_15;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer> {

	// 定義個變量
	private int number;

	public MyCallable(int number) {
		this.number = number;
	}

	@Override
	public Integer call() throws Exception {
		// 定義最終結果變量
		int sum = 0;
		for (int x = 1; x <= number; x++) {
			sum += x;
		}
		return sum;
	}

}
















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章