【JavaSE筆記】多線程(二)多線程

本期知識點:
多線程
線程組
線程池
匿名內部類
定時器


1.多線程

a.JDK5以後的針對線程的鎖定操作和釋放操作

使用同步機制解決了線程的安全問題,但是我們並沒有看到具體的鎖對象是誰,JDK5以後java提供了接口
Lock裏面又提供了一些方法:
Lock 實現提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作
由於該Lock接口不能實例化,提供了子實現類:ReentrantLock
public void lock()   獲取鎖
public void unlock()   釋放鎖

b.死鎖

i.雖然使用Lock鎖定操作或者是同步鎖synchronized來解決線程安全的問題,線程安全解決了,
線程安全的弊端:
1)效率低
2)如果出現了同步嵌套,就容易產生死鎖問題
ii.死鎖問題的產生:
兩個或兩個以上的線程,搶佔CPU的執行權,然後出現了互相等待的情況:使用線程間的通信問題解決!
iii.死鎖問題及其代碼
是指兩個或者兩個以上的線程在執行的過程中,因爭奪資源產生的一種互相等待現象

c.線程間通信問題:

不同種類的線程間針對同一個資源的操作。
分析:當前的資源情況:
Student: 共同的資源
setThread:設置學生數據(生成者)
getThread:獲取學生數據(消費者)
StudentDemo(測試類)
問題1:
按照生產消費模式:分別進行產生數據和消費數據,通過測試打印出來:

null---0


問題2:
爲了數據的效果好,加入循環和判斷,給出不同的值,這個時候產生新的問題。
a.同一個數據出現多次
原因: CPU的一點點時間片的執行權,就足夠執行很多次。
b.年齡和姓名不匹配
原因:線程運行的隨機性
解決方法:

加鎖

注意:

i.不同種類的線程都要加鎖

ii.不同種類的線程必須加同一把鎖


問題3:線程安全問題解決了,但是會存在如下問題
i.如果消費者先搶到了CPU的執行權,就會去消費數據,但是現在的數據是默認值。沒有意義應該等待數據有意義再去消費。
ii.如果生產者搶到CPU的執行權,就會去生產數據,但是它生產完數據後依然擁有執行權,那麼它繼續生產數據。這樣就有問題,應該等消費者把數據消費完,再生產。
正常思路:
1)生產者:先看是否有數據,有就等待,沒有就生產,生產完後通知消費者來消費數據。
2)消費者:先看是否有數據,有就消費,沒有就等待,通知生產者生產數據。
爲了處理這樣的問題,Java提供了一種機制:等待喚醒機制
d.等待喚醒機制
i.Object類中提供了一些方法:
wait()   線程等待
public final void notify()   喚醒正在等待的單個線程
public final void notifyAll()   喚醒所有線程
ii.(面試題)這幾個方法都是線程有關的方法,爲什麼把這個方法不定一在Thread類裏面?

剛纔這個案例,使用的鎖對象進行調用,鎖對象可以是任意對象.而Object本身就是代表所有的類根類:代表所有對象.


e.(問題)wait(),notify(),notifyAll(),用來操作線程爲什麼定義在了Object類中?

i.這些方法存在與同步中。
ii.使用這些方法時必須要標識所屬的同步的鎖。

iii.鎖可以是任意對象,所以任意對象調用的方法一定定義Object類中。


f.線程的狀態轉移圖:




死鎖:

public class MyLock {
	public static final Object objectA = new Object();
	public static final Object objectB = new Object();
	
}
public class DieLock extends Thread {
	private boolean flag;
	public DieLock(boolean flag) {
		this.flag=flag;
	}
	@Override
	public void run() {
		if(flag){
			synchronized (MyLock.objectA) {
				System.out.println("if ObjectA");
				synchronized (MyLock.objectB){
					System.out.println("if ObjectB");
				}
			}
		}
		else{
			synchronized (MyLock.objectB){
				System.out.println("else ObjectB");
				synchronized (MyLock.objectA){
					System.out.println("else Object");
				}
			}
		}
	}
}
public class DieLockDemo {
	public static void main(String[] args) {
		//創建線程對象
		DieLock d1 = new DieLock(true);
		DieLock d2 = new DieLock(false);
		//啓動線程
		d1.start();
		d2.start();
	}
}


等待喚醒機制:

//生產者
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();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				if(x%2==0){
					s.name="Ash"; 
					s.age=24;
				}else{
					s.name="Ying"; 
					s.age=22;
				}
				x++;
				//修改標記
				s.flag=true;
				//喚醒線程
				s.notify();
			}
		}
	}
}
//消費者
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();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				System.out.println(s.name+"---"+s.age);
				//修改標記
				s.flag=false;
				//喚醒線程
				s.notify();
			}
		}
	}

}
public class Student {
	String name;
	int age;
	boolean flag;	
}
public class StudentDemo {
	public static void main(String[] args) {
		//創建共同資源對象
		Student s = new Student();
		//創建每個線程對象,針對同一個資源進行操作
		SetThread st = new SetThread(s);
		GetThread gt = new GetThread(s);
		//創建Thread類對象
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(gt);
		//分別啓動線程
		t1.start();
		t2.start();	
	}
}


2.線程組

a.概述

Java中使用ThreadGroup來表示線程組,它可以對一批線程進行分類管理,Java允許程序直接對線程組進行控制。

b.常用方法:

public ThreadGroup(String name)   構造一個新線程組。
public Thread(ThreadGroup group,Runnable target, String name)
public final void setDaemon(boolean daemon)   設置線程組是否是一個守護線程
public final ThreadGroup getThreadGroup()   返回該線程所在的線程組。默認情況下,所有的線程都屬於主線程組。

public final String getName()   返回此線程組的名稱。

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		for (int i = 0; i < 10 ; i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}
}
public class ThreadGroupDemo {
	public static void main(String[] args) {
		//fun1();
		fun2();
	}
	private static void fun2() {
		//不知道t1,t2線程是屬於哪一個線程組的?
		//如何該線程所在線程組的名稱
		MyRunnable my = new MyRunnable() ;
		
		Thread t1 = new Thread(my, "張三") ;
		Thread t2 = new Thread(my, "李四") ;
		
		//Thread類中提供了另外一個方法:
//		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());
	}
	private static void fun1() {
		//如何設置線程組名稱?
//		public ThreadGroup(String name)構造一個新線程組。
		ThreadGroup tg = new ThreadGroup("這是一個新的線程組") ;
		
		MyRunnable my = new MyRunnable();
		Thread t1 = new Thread(my ,"Ash");
		Thread t2 = new Thread(my ,"Glaz");
		
		ThreadGroup tg1 = t1.getThreadGroup();
		ThreadGroup tg2 = t2.getThreadGroup();
		
		String name1 = tg1.getName() ;
		String name2 = tg1.getName() ;
		System.out.println(name1);
		System.out.println(name2);
		
		tg.setDaemon(true);
	}
}



3.線程池

a.概述:

程序啓動一個新線程成本是比較高的,因爲它涉及到要與操作系統進行交互。而使用線程池可以很好的提高性能,尤其是當程序中要創建大量生存期很短的線程時,更應該考慮使用線程池。

b.使用線程池可以解決很多問題:

1)如何創建線程池對象:
Executors工廠:專門用來創建線程池的:提供了一個方法
public static ExecutorService newFixedThreadPool(int nThreads)
2)這些方法的返回值是ExecutorService對象,該對象表示一個線程池,可以執行Runnable對象或者Callable對象代表的線程。它提供瞭如下方法
Future<?> submit(Runnable task)   Runnable接口作爲一個參數:要該類的子實現類對象
<T> Future<T> submit(Callable<T> task)
3)線程池可以結束嗎?
void shutdown()

c.多線程程序實現方法三:

步驟:
1)自定義一個類,實現Callable接口
2)實現裏面的call方法
3)主線程中創建線程對象,
4)用線程池對象提交任務(例如:MyCallable這個任務實現0--100之間的循環)

5)提交後結束線程池

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		for (int i = 0; i < 100 ; i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorsDemo {
	public static void main(String[] args) {
		//創建線程池對象
		ExecutorService pool = Executors.newFixedThreadPool(3);
		//使用ExecutorsService接口中有要給方法:
		//Future<?> submit(Runnable task):Runnable接口作爲一個參數:要該類的子實現類對象
		pool.submit(new MyRunnable());
		pool.submit(new MyRunnable());
		pool.submit(new MyRunnable());
		//結束線程池
		pool.shutdown();
	}
}


4.匿名內部類

匿名內部類的方式實現多線程程序:

new 類名或者接口名{(

重寫/實現方法;

}

本質:繼承該類或者或者實現該接口的子類對象!


5.定時器

a.概述:

定時器是一個應用十分廣泛的線程工具,可用於調度多個定時任務以後臺線程的方式執行。在Java中,可以通過Timer和TimerTask類來實現定義調度的功能

b.構造方法:a

public Timer()   創建一個新計時器。
public void schedule(TimerTask task, Date time)   安排在指定的時間執行指定的任務
參數1:task - 所要安排的任務
參數2:time - 執行該任務的時間毫秒值

c.其他方法

public boolean cancel()   取消此計時器任務
public void schedule(TimerTask task, Date firstTime,long period)   每隔多少毫秒進行重複性的 任務

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

public class Demo01_無參調用 {
	public static void main(String[] args) {
		Timer t = new Timer();
		t.schedule(new Fun(), 3000);
	}	
}
class Fun extends TimerTask{
	public Fun(){
		
	}
	@Override
	public void run() {
		System.out.println("boom!!!");
	}
}

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

public class Demo02_有參調用 {
	public static void main(String[] args) {
		//創建定時器對象
		Timer t = new Timer();
		t.schedule(new Hun(t), 3000);
	}
}

class Hun extends TimerTask{
	private Timer t;
	public Hun(){
		
	}
	public Hun(Timer t){
		this.t=t;
	}
	@Override
	public void run() {
		System.out.println("boom!!!");
		t.cancel();
	}
}

import java.io.File;
import java.util.Timer;
import java.util.TimerTask;

//在指定的時間刪除我們的指定目錄(你可以指定c盤,但是我不建議,我使用項目路徑下的demo)

public class test {
	public static void main(String[] args) {
		File f = new File("C:\\Users\\Administrator\\Desktop\\Demo");
		Timer t = new Timer();
		t.schedule(new Fun3(t,f), 3000);
	}
}
class Fun3 extends TimerTask{
	
	private Timer t ;
	private File f;
	public Fun3(Timer t, File f) {
		this.t=t;
		this.f=f;
	}

	@Override
	public void run() {
		hun(f);
		t.cancel();
	}
	public void hun(File f){
		File[] fa = f.listFiles();
		//非空判斷
		if(fa!=null){
			for (File file : fa) {
				if(file.isDirectory()){
					hun(file);
				}else{
					System.out.println(file.getName()+":"+file.delete());
				}
			}
			System.out.println(f.getName()+":"+f.delete());
		}
	}
}

發佈了51 篇原創文章 · 獲贊 15 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章