day24-----------多線程(傳智視頻)

JDK5以後的LOCK鎖的概述和使用
java

package cn.itcast_01;

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

public class SellTicket implements Runnable {

	// 定義票
	private int tickets = 100;

	// 定義鎖對象
	private Lock lock = new ReentrantLock();

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

}
package cn.itcast_01;
/*
 * 雖然咱們能夠理解同步代碼塊和同步方法的鎖對象問題,可是咱們並無直接看到在哪裏加上了鎖,在哪裏釋放了鎖,
 * 爲了更清晰的表達如何加鎖和釋放鎖,JDK5之後提供了一個新的鎖對象Lock。
 * 
 * Lock:
 * 		void lock(): 獲取鎖。
 * 		void unlock():釋放鎖。  
 * ReentrantLock是Lock的實現類.
 */
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();
	}
}

生產者消費者題代碼2並解決線程安全問題
面試

package cn.itcast_04;

public class Student {
	String name;
	int age;
}
package cn.itcast_04;

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 = 30;
				}
				x++;
			}
		}
	}
}
package cn.itcast_04;

public class GetThread implements Runnable {
	private Student s;

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

	@Override
	public void run() {
		while (true) {
			synchronized (s) {
				System.out.println(s.name + "---" + s.age);
			}
		}
	}
}
package cn.itcast_04;

/*
 * 分析:
 * 		資源類:Student	
 * 		設置學生數據:SetThread(生產者)
 * 		獲取學生數據:GetThread(消費者)
 * 		測試類:StudentDemo
 * 
 * 問題1:按照思路寫代碼,發現數據每次都是:null---0
 * 緣由:咱們在每一個線程中都建立了新的資源,而咱們要求的時候設置和獲取線程的資源應該是同一個
 * 如何實現呢?
 * 		在外界把這個數據建立出來,經過構造方法傳遞給其餘的類。
 * 
 * 問題2:爲了數據的效果好一些,我加入了循環和判斷,給出不一樣的值,這個時候產生了新的問題
 * 		A:同一個數據出現屢次
 * 		B:姓名和年齡不匹配
 * 緣由:
 * 		A:同一個數據出現屢次
 * 			CPU的一點點時間片的執行權,就足夠你執行不少次。
 * 		B:姓名和年齡不匹配
 * 			線程運行的隨機性
 * 線程安全問題:
 * 		A:是不是多線程環境		是
 * 		B:是否有共享數據		是
 * 		C:是否有多條語句操做共享數據	是
 * 解決方案:
 * 		加鎖。
 * 		注意:
 * 			A:不一樣種類的線程都要加鎖。
 * 			B:不一樣種類的線程加的鎖必須是同一把。
 */
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();
	}
}

生成者消費者之等待喚醒機制思路圖
安全

生產者消費者之等待喚醒機制代碼分析
多線程

package cn.itcast_05;

public class Student {
	String name;
	int age;
	boolean flag; // 默認狀況是沒有數據,若是是true,說明有數據
}
package cn.itcast_05;

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(s.flag){
					try {
						s.wait(); //t1等着,釋放鎖
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
				if (x % 2 == 0) {
					s.name = "林青霞";
					s.age = 27;
				} else {
					s.name = "劉意";
					s.age = 30;
				}
				x++; //x=1
				
				//修改標記
				s.flag = true;
				//喚醒線程
				s.notify(); //喚醒t2,喚醒並不表示你立馬能夠執行,必須還得搶CPU的執行權。
			}
			//t1有,或者t2有
		}
	}
}
package cn.itcast_05;

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){
					try {
						s.wait(); //t2就等待了。當即釋放鎖。未來醒過來的時候,是從這裏醒過來的時候
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
				System.out.println(s.name + "---" + s.age);
				//林青霞---27
				//劉意---30
				
				//修改標記
				s.flag = false;
				//喚醒線程
				s.notify(); //喚醒t1
			}
		}
	}
}
package cn.itcast_05;

/*
 * 分析:
 * 		資源類:Student	
 * 		設置學生數據:SetThread(生產者)
 * 		獲取學生數據:GetThread(消費者)
 * 		測試類:StudentDemo
 * 
 * 問題1:按照思路寫代碼,發現數據每次都是:null---0
 * 緣由:咱們在每一個線程中都建立了新的資源,而咱們要求的時候設置和獲取線程的資源應該是同一個
 * 如何實現呢?
 * 		在外界把這個數據建立出來,經過構造方法傳遞給其餘的類。
 * 
 * 問題2:爲了數據的效果好一些,我加入了循環和判斷,給出不一樣的值,這個時候產生了新的問題
 * 		A:同一個數據出現屢次
 * 		B:姓名和年齡不匹配
 * 緣由:
 * 		A:同一個數據出現屢次
 * 			CPU的一點點時間片的執行權,就足夠你執行不少次。
 * 		B:姓名和年齡不匹配
 * 			線程運行的隨機性
 * 線程安全問題:
 * 		A:是不是多線程環境		是
 * 		B:是否有共享數據		是
 * 		C:是否有多條語句操做共享數據	是
 * 解決方案:
 * 		加鎖。
 * 		注意:
 * 			A:不一樣種類的線程都要加鎖。
 * 			B:不一樣種類的線程加的鎖必須是同一把。
 * 
 * 問題3:雖然數據安全了,可是呢,一次一大片很差看,我就想依次的一次一個輸出。
 * 如何實現呢?
 * 		經過Java提供的等待喚醒機制解決。
 * 
 * 等待喚醒:
 * 		Object類中提供了三個方法:
 * 			wait():等待
 * 			notify():喚醒單個線程  //理解是解開當前的鎖,由於只執行當前的進程,即喚醒了全部線程
 * 			notifyAll():喚醒全部線程
 * 		爲何這些方法不定義在Thread類中呢?
 * 			這些方法的調用必須經過鎖對象調用,而咱們剛纔使用的鎖對象是任意鎖對象。
 * 			因此,這些方法必須定義在Object類中。
 */
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();
	}
}

線程的狀態轉換圖及常見執行狀況
ide


線程組的概述和使用
測試

package cn.itcast_06;

/*
 * 線程組: 把多個線程組合到一塊兒。
 * 它能夠對一批線程進行分類管理,Java容許程序直接對線程組進行控制。
 */
public class ThreadGroupDemo {
	public static void main(String[] args) {
		// method1();

		// 咱們如何修改線程所在的組呢?
		// 建立一個線程組
		// 建立其餘線程的時候,把其餘線程的組指定爲咱們本身新建線程組
		method2();

		// t1.start();
		// t2.start();
	}

	private static void method2() {
		// ThreadGroup(String name)
		ThreadGroup tg = new ThreadGroup("這是一個新的組");

		MyRunnable my = new MyRunnable();
		// Thread(ThreadGroup group, Runnable target, String name)
		Thread t1 = new Thread(tg, my, "林青霞");
		Thread t2 = new Thread(tg, my, "劉意");
		
		System.out.println(t1.getThreadGroup().getName());
		System.out.println(t2.getThreadGroup().getName());
		
		//經過組名稱設置後臺線程,表示該組的線程都是後臺線程
		tg.setDaemon(true);
	}

	private static void method1() {
		MyRunnable my = new MyRunnable();
		Thread t1 = new Thread(my, "林青霞");
		Thread t2 = new Thread(my, "劉意");
		// 我不知道他們屬於那個線程組,我想知道,怎麼辦
		// 線程類裏面的方法:public final ThreadGroup getThreadGroup()
		ThreadGroup tg1 = t1.getThreadGroup();
		ThreadGroup tg2 = t2.getThreadGroup();
		// 線程組裏面的方法:public final String getName()
		String name1 = tg1.getName();
		String name2 = tg2.getName();
		System.out.println(name1);
		System.out.println(name2);
		// 經過結果咱們知道了:線程默認狀況下屬於main線程組
		// 經過下面的測試,你應該可以看到,默任狀況下,全部的線程都屬於同一個組
		System.out.println(Thread.currentThread().getThreadGroup().getName());
	}
}

生成者消費者代碼優化
優化

package cn.itcast_07;

public class Student {
	private String name;
	private int age;
	private boolean flag; // 默認狀況是沒有數據,若是是true,說明有數據

	public synchronized void set(String name, int age) {
		// 若是有數據,就等待
		if (this.flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		// 設置數據
		this.name = name;
		this.age = age;

		// 修改標記
		this.flag = true;
		this.notify();
	}

	public synchronized void get() {
		// 若是沒有數據,就等待
		if (!this.flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		// 獲取數據
		System.out.println(this.name + "---" + this.age);

		// 修改標記
		this.flag = false;
		this.notify();
	}
}
package cn.itcast_07;

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) {
			if (x % 2 == 0) {
				s.set("林青霞", 27);
			} else {
				s.set("劉意", 30);
			}
			x++;
		}
	}
}
package cn.itcast_07;

public class GetThread implements Runnable {
	private Student s;

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

	@Override
	public void run() {
		while (true) {
			s.get();
		}
	}
}
package cn.itcast_07;

/*
 * 分析:
 * 		資源類:Student	
 * 		設置學生數據:SetThread(生產者)
 * 		獲取學生數據:GetThread(消費者)
 * 		測試類:StudentDemo
 * 
 * 問題1:按照思路寫代碼,發現數據每次都是:null---0
 * 緣由:咱們在每一個線程中都建立了新的資源,而咱們要求的時候設置和獲取線程的資源應該是同一個
 * 如何實現呢?
 * 		在外界把這個數據建立出來,經過構造方法傳遞給其餘的類。
 * 
 * 問題2:爲了數據的效果好一些,我加入了循環和判斷,給出不一樣的值,這個時候產生了新的問題
 * 		A:同一個數據出現屢次
 * 		B:姓名和年齡不匹配
 * 緣由:
 * 		A:同一個數據出現屢次
 * 			CPU的一點點時間片的執行權,就足夠你執行不少次。
 * 		B:姓名和年齡不匹配
 * 			線程運行的隨機性
 * 線程安全問題:
 * 		A:是不是多線程環境		是
 * 		B:是否有共享數據		是
 * 		C:是否有多條語句操做共享數據	是
 * 解決方案:
 * 		加鎖。
 * 		注意:
 * 			A:不一樣種類的線程都要加鎖。
 * 			B:不一樣種類的線程加的鎖必須是同一把。
 * 
 * 問題3:雖然數據安全了,可是呢,一次一大片很差看,我就想依次的一次一個輸出。
 * 如何實現呢?
 * 		經過Java提供的等待喚醒機制解決。
 * 
 * 等待喚醒:
 * 		Object類中提供了三個方法:
 * 			wait():等待
 * 			notify():喚醒單個線程
 * 			notifyAll():喚醒全部線程
 * 		爲何這些方法不定義在Thread類中呢?
 * 			這些方法的調用必須經過鎖對象調用,而咱們剛纔使用的鎖對象是任意鎖對象。
 * 			因此,這些方法必須定義在Object類中。
 * 
 * 最終版代碼中:
 * 		把Student的成員變量給私有的了。
 * 		把設置和獲取的操做給封裝成了功能,並加了同步。
 * 		設置或者獲取的線程裏面只須要調用方法便可。
 */
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();
	}
}

線程池的概述和使用
this

package cn.itcast_08;

public class MyRunnable implements Runnable {

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

}
package cn.itcast_08;

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

/*
 * 線程池的好處:線程池裏的每個線程代碼結束後,並不會死亡,而是再次回到線程池中成爲空閒狀態,等待下一個對象來使用。
 * 
 * 如何實現線程的代碼呢?
 * 		A:建立一個線程池對象,控制要建立幾個線程對象。
 * 			public static ExecutorService newFixedThreadPool(int nThreads)
 * 		B:這種線程池的線程能夠執行:
 * 			能夠執行Runnable對象或者Callable對象表明的線程
 * 			作一個類實現Runnable接口。
 * 		C:調用以下方法便可
 * 			Future<?> submit(Runnable task)
 *			<T> Future<T> submit(Callable<T> task)
 *		D:我就要結束,能夠嗎?
 *			能夠。
 */
public class ExecutorsDemo {
	public static void main(String[] args) {
		// 建立一個線程池對象,控制要建立幾個線程對象。
		// public static ExecutorService newFixedThreadPool(int nThreads)
		ExecutorService pool = Executors.newFixedThreadPool(2);

		// 能夠執行Runnable對象或者Callable對象表明的線程
		pool.submit(new MyRunnable());
		pool.submit(new MyRunnable());

		//結束線程池
		pool.shutdown();
	}
}

多線程方式3線程

package cn.itcast_09;

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 cn.itcast_09;

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

/*
 * 多線程實現的方式3:
 *  	A:建立一個線程池對象,控制要建立幾個線程對象。
 * 			public static ExecutorService newFixedThreadPool(int nThreads)
 * 		B:這種線程池的線程能夠執行:
 * 			能夠執行Runnable對象或者Callable對象表明的線程
 * 			作一個類實現Runnable接口。
 * 		C:調用以下方法便可
 * 			Future<?> submit(Runnable task)
 *			<T> Future<T> submit(Callable<T> task)
 *		D:我就要結束,能夠嗎?
 *			能夠。
 */
public class CallableDemo {
	public static void main(String[] args) {
		//建立線程池對象
		ExecutorService pool = Executors.newFixedThreadPool(2);
		
		//能夠執行Runnable對象或者Callable對象表明的線程
		pool.submit(new MyCallable());
		pool.submit(new MyCallable());
		
		//結束
		pool.shutdown();
	}
}

線程方式3的求和案例
code

package cn.itcast_10;

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;
	}

}
package cn.itcast_10;

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

/*
 * 多線程實現的方式3:
 *  	A:建立一個線程池對象,控制要建立幾個線程對象。
 * 			public static ExecutorService newFixedThreadPool(int nThreads)
 * 		B:這種線程池的線程能夠執行:
 * 			能夠執行Runnable對象或者Callable對象表明的線程
 * 			作一個類實現Runnable接口。
 * 		C:調用以下方法便可
 * 			Future<?> submit(Runnable task)
 *			<T> Future<T> submit(Callable<T> task)
 *		D:我就要結束,能夠嗎?
 *			能夠。
 */
public class CallableDemo {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		// 建立線程池對象
		ExecutorService pool = Executors.newFixedThreadPool(2);

		// 能夠執行Runnable對象或者Callable對象表明的線程
		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 cn.itcast_11;

/*
 * 匿名內部類的格式:
 * 		new 類名或者接口名() {
 * 			重寫方法;
 * 		};
 * 		本質:是該類或者接口的子類對象。
 */
public class ThreadDemo {
	public static void main(String[] args) {
		// 繼承Thread類來實現多線程
		new Thread() {
			public void run() {
				for (int x = 0; x < 100; x++) {
					System.out.println(Thread.currentThread().getName() + ":"
							+ x);
				}
			}
		}.start();

		// 實現Runnable接口來實現多線程
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int x = 0; x < 100; x++) {
					System.out.println(Thread.currentThread().getName() + ":"
							+ x);
				}
			}
		}) {
		}.start();

		// 更有難度的
		// 面試題:若是兩個的話,執行的是方法體中的,也就是輸出的是world的部分
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int x = 0; x < 100; x++) {
					System.out.println("hello" + ":" + x);
				}
			}
		}) {
			public void run() {
				for (int x = 0; x < 100; x++) {
					System.out.println("world" + ":" + x);
				}
			}
		}.start();
	}
}

定時器的概述和使用

package cn.itcast_12;

import java.util.Timer;
import java.util.TimerTask;

/*
 * 定時器:可讓咱們在指定的時間作某件事情,還能夠重複的作某件事情。
 * 依賴Timer和TimerTask這兩個類:
 * Timer:定時
 * 		public Timer()
 * 		public void schedule(TimerTask task,long delay)
 * 		public void schedule(TimerTask task,long delay,long period)
 * 		public void cancel()
 * TimerTask:任務
 */
public class TimerDemo {
	public static void main(String[] args) {
		// 建立定時器對象
		Timer t = new Timer();
		// 3秒後執行爆炸任務
		// t.schedule(new MyTask(), 3000);
		//結束任務
		t.schedule(new MyTask(t), 3000);
	}
}

// 作一個任務
class MyTask extends TimerTask {

	private Timer t;
	
	public MyTask(){}
	
	public MyTask(Timer t){
		this.t = t;
	}
	
	@Override
	public void run() {
		System.out.println("beng,爆炸了");
		t.cancel();
	}

}

定時任務的屢次執行代碼

package cn.itcast_12;

import java.util.Timer;
import java.util.TimerTask;

/*
 * 定時器:可讓咱們在指定的時間作某件事情,還能夠重複的作某件事情。
 * 依賴Timer和TimerTask這兩個類:
 * Timer:定時
 * 		public Timer()
 * 		public void schedule(TimerTask task,long delay)
 * 		public void schedule(TimerTask task,long delay,long period)
 * 		public void cancel()
 * TimerTask:任務
 */
public class TimerDemo2 {
	public static void main(String[] args) {
		// 建立定時器對象
		Timer t = new Timer();
		// 3秒後執行爆炸任務第一次,若是不成功,每隔2秒再繼續炸
		t.schedule(new MyTask2(), 3000, 2000);
	}
}

// 作一個任務
class MyTask2 extends TimerTask {
	@Override
	public void run() {
		System.out.println("beng,爆炸了");
	}
}

定時刪除指定的帶內容目錄

package cn.itcast_12;

import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/*
 * 需求:在指定的時間刪除咱們的指定目錄(你能夠指定c盤,可是我不建議,我使用項目路徑下的demo)
 */

class DeleteFolder extends TimerTask {

	@Override
	public void run() {
		File srcFolder = new File("demo");
		deleteFolder(srcFolder);
	}

	// 遞歸刪除目錄
	public void deleteFolder(File srcFolder) {
		File[] fileArray = srcFolder.listFiles();
		if (fileArray != null) {
			for (File file : fileArray) {
				if (file.isDirectory()) {
					deleteFolder(file);
				} else {
					System.out.println(file.getName() + ":" + file.delete());
				}
			}
			System.out.println(srcFolder.getName() + ":" + srcFolder.delete());
		}
	}
}

public class TimerTest {
	public static void main(String[] args) throws ParseException {
		Timer t = new Timer();

		String s = "2014-11-27 15:45:00";
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		Date d = sdf.parse(s);

		t.schedule(new DeleteFolder(), d);
	}
}

多線程常見面試題

1:多線程有幾種實現方案,分別是哪幾種?
	兩種。
	
	繼承Thread類
	實現Runnable接口
	
	擴展一種:實現Callable接口。這個得和線程池結合。

2:同步有幾種方式,分別是什麼?
	兩種。
	
	同步代碼塊
	同步方法

3:啓動一個線程是run()仍是start()?它們的區別?
	start();
	
	run():封裝了被線程執行的代碼,直接調用僅僅是普通方法的調用
	start():啓動線程,並由JVM自動調用run()方法

4:sleep()和wait()方法的區別
	sleep():必須指時間;不釋放鎖。
	wait():能夠不指定時間,也能夠指定時間;釋放鎖。

5:爲何wait(),notify(),notifyAll()等方法都定義在Object類中
	由於這些方法的調用是依賴於鎖對象的,而同步代碼塊的鎖對象是任意鎖。
	而Object代碼任意的對象,因此,定義在這裏面。

6:線程的生命週期圖
	新建 -- 就緒 -- 運行 -- 死亡
	新建 -- 就緒 -- 運行 -- 阻塞 -- 就緒 -- 運行 -- 死亡
	建議:畫圖解釋。
相關文章
相關標籤/搜索