Java學習總結1——多線程編程

Java多線程編程

1、建立線程的方法一:編程

繼承:extends Thread 重寫run()方法安全

舉個栗子🌰:bash

public class MyThread extends Thread {
	public MyThread() {
		//空的構造方法
	}
	
	//傳遞name表示線程名字
	public MyThread(String name) {
		super(name);
	}

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			if (interrupted()) {
				System.out.println("釋放資源!");
				break;
			}
			System.out.println(getName()+":"+i);
		}
	}
}
複製代碼
MyThread t1 = new MyThread(「線程一」);
MyThread t2 = new MyThread(「線程二」);

Thread thread = new Thread();
thread.start();//啓動線程

thread.getPriority();//獲取優先級
thread.setPriority();//設置優先級(1-10)

thread .getName();//獲取線程名字
thread .setName();//設置線程名字
複製代碼

線程休眠:微信

Thread.sleep(4000);//主線程休眠四秒以後再運行
複製代碼

線程加入:多線程

t1.join();//將線程t1加入主線程
複製代碼

設置守護線程:app

t2.setDaemon(true);將t2線程設置爲守護線程(守護線程須要在線程啓動以前設置),守護線程是爲守護其餘線程而存在,若是其餘全部線程都運行完以後,只還剩下守護線程沒運行完,那麼守護線程將自動銷燬!ide

線程的中斷:ui

t1.interrupt();//手動處理線程中斷
interrupted();//將返回布爾值
複製代碼

線程的生命週期: this

線程的生命週期


2、建立線程的方法二:spa

實現Runnable接口: implement Runnable

public class RunnableThread implements Runnable {

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			Thread thread = Thread.currentThread();//獲取當前線程名字
			System.out.println(thread.getName()+":"+i);
		}
		
	}

}
複製代碼

該方法不能直接使用start()方法啓動線程。 須要使用Thread聲明一個對象,再將runnable對象傳入Thread對象。

RunnableThread t = new RunnableThread();

Thread t1 = new Thread(t,」線程一」);
t1.start();
		
Thread t2 = new Thread(t,」線程二」);
t2.start();
複製代碼

兩種建立線程方法區別: runnable方法只須要建立一個runnable對象,Thread方法須要建立多個MyThread對象。Thread方法獲取線程其餘參數更方便。兩種方法都須要重寫run()方法。

使用Runnable方法建立線程的好處:

  1. 能夠很方便的實現多線程數據的共享。
  2. 該方法還能夠繼承其餘的類,而繼承Thread類的方法不能再繼承其餘的類了。

3、建立線程的方法三:

匿名內部類: 直接在主類裏面定義一個Runnable類來實現Runnable接口裏面的run()方法。

適用於該線程在主類裏面只使用一次的狀況,不用再建立一個線程類來實現Runnable接口。

線程安全問題: 當多個線程須要同時修改同一共享數據時,產生衝突,就會出現線程安全問題。

  1. 加鎖解決:
private Object lock = new Object();//須要聲明一個對象做爲鎖
複製代碼
//加鎖
synchronized (lock) {
	//須要加鎖執行的代碼
}//執行完畢,歸還鑰匙
複製代碼

也能夠直接使用同步方法:sellTicket()

public synchronized void sellTicket(){
//須要加鎖的代碼
}//至關於給這個方法加了鎖
複製代碼

同步方法不須要再聲明一個對象做爲鎖了。

  1. 使用專門的鎖對象ReentrantLock來加鎖:
private ReentrantLock lock = new ReentrantLock();
複製代碼
lock.lock();//加鎖
//須要加鎖的代碼
lock.unlock();//解鎖
複製代碼

死鎖問題的解決:

多個線程以相同的順序去取鎖,取到全部鎖以後才執行須要加鎖的代碼!

舉個栗子🌰:

public class DeadLock {
	//建立兩把鎖
	public static Object lock1 = new Object();
	public static Object lock2 = new Object();
	
	public static void main(String[] args) {
		//啓動兩個線程
		new Thread(new Thread1()).start();
		new Thread(new Thread2()).start();
	}
}

//匿名內部類實現Runnable建立Thread1線程
class Thread1 implements Runnable{
	
	@Override
	public void run() {
		synchronized (DeadLock.lock1) {//先取第一把鎖
			System.out.println("Thread1取得了第一把鎖以後要作的事情");
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			synchronized (DeadLock.lock2) {//再取第二把鎖
				System.out.println("Thread1同時取得兩把鎖以後要作的事情");
			}
		}
	}
	
}

//匿名內部類實現Runnable建立Thread2線程
class Thread2 implements Runnable{

	@Override
	public void run() {
		synchronized (DeadLock.lock1) {//先取第一把鎖
			System.out.println("Thread2取得了第二把鎖以後要作的事情");
			synchronized (DeadLock.lock2) {//再取第二把鎖
				System.out.println("Thread2同時取得兩把鎖以後要作的事情");
			}
		}
	}
	
}
複製代碼

建立線程組:ThreadGroup

ThreadGroup tg = new ThreadGroup(「個人線程組」);
複製代碼

定時器:Timer

須要在計時器任務裏面設置任務:TimerTask

Timer timer = new Timer();//定時器
timer.schedule(new MyTimer(), 2000);//2s後啓動計時器任務
timer.schedule(new MyTimer(), 2000, 1000);//2s啓動後,隔3s後再重複執行計時器任務
timer.cancel();//取消定時器
複製代碼

舉個栗子🌰:

public class Demo_Timer {
	public static void main(String[] args) {
		Timer timer = new Timer();//定時器
		
		//timer.schedule(new MyTimer(), 2000);
		timer.schedule(new MyTimer(), 2000, 1000);
		//timer.cancel();//取消定時器
		
	}
}

//定時器任務(匿名內部類)
class MyTimer extends TimerTask{

	@Override
	public void run() {
		System.out.println("定時器任務..");
	}
		
}
複製代碼

4、綜合實例:

電影院售票問題: 說明:兩種買票方式,手機APP端和電影院窗口端同時售票,採用建立多線程的方式來解決電影票多端售出產生的衝突!

採用實現Runnable接口的方法建立線程。

一、建立AppThread類:(APP端線程類)

public class AppThread implements Runnable {
	private Object lock;
	
	public AppThread(Object lock) {
		this.lock = lock;
	}
	
	@Override
	public void run() {
		while (MovieTicketManage.count>0) {
			synchronized (lock) {
				if (MovieTicketManage.count>0) {
					System.out.println(Thread.currentThread().getName()+"售出了第"+MovieTicketManage.count+"張電影票..");
					MovieTicketManage.count--;
				}
			}
			
			try {
				Thread.sleep(0);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
	}

}
複製代碼

二、建立WindowThread類:(window端線程類)

public class WindowThread implements Runnable {
	private Object lock;
	
	public WindowThread(Object lock) {
		this.lock = lock;
	}
	
	@Override
	public void run() {
		while (MovieTicketManage.count>0) {
			synchronized (lock) {
				if (MovieTicketManage.count>0) {
					System.out.println(Thread.currentThread().getName()+"售出了第"+MovieTicketManage.count+"張電影票..");
					MovieTicketManage.count--;
				}
			}
			
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}

	}

}
複製代碼

三、建立MovieTicketManage類:(票管理類,存儲電影票數量,爲了多線程數據的共享)

public class MovieTicketManage {
	public static int count = 100;
}
複製代碼

四、建立CinemaSale主類:(電影票售票主類)

public class CinemaSale {
	public static void main(String[] args) {
		Object lock = new Object();
		
		WindowThread windowThread = new WindowThread(lock);
		AppThread appThread = new AppThread(lock);
		
		new Thread(windowThread,"窗口").start();
		new Thread(appThread,"APP手機").start(); 
	}
}
複製代碼

須要建立一把相同的鎖lock,將這把鎖傳入AppThread和WindowThread線程類,保證鎖的惟一,若是在AppThread線程和WindowThread線程裏面建立鎖,那麼就是不一樣對象的鎖了。就不能保證共享數據的同步,經過定義一個成員變量lock,經過構造方法將這個鎖傳遞過來.

這樣在購買電影票的時候就不會產生線程衝突和死鎖問題了!😁

今日寄語:

去作那些讓你緊張的事情,當你克服了這段焦慮以後,你會發現,一切沒你想象的那麼困難。💪

歡迎關注我的微信公衆號:桃李報春

相關文章
相關標籤/搜索