Java 多線程大全

一,簡介

1,什麼是線程

     線程是程序執行的一條路徑, 一個進程中可以包含多條線程

     多線程併發執行可以提高程序的效率, 可以同時完成多項工作

     有時稱爲輕量級進程,是CPU使用的基本單元;

2,線程組成

      由線程ID、程序計數器、寄存器集合和堆棧組成。

3,多線程的應用場景

     迅雷開啓多條線程一起下載
     QQ同時和多個人一起視頻
     服務器同時處理多個客戶端請求

4,併發和並行的區別

      並行就是兩個任務同時運行,就是甲任務進行的同時,乙任務也在進行。(需要多核CPU)
      併發是指兩個任務都請求運行,而處理器只能按受一個任務,就把這兩個任務安排輪流進行,由於時間間隔較短,使人感覺兩個任務都在運行。
      比如我跟兩個網友聊天,左手操作一個手機跟甲聊,同時右手用另一臺電腦跟乙聊天,這就叫並行。
      如果用一部手機先給甲發個消息,然後立刻再給乙發消息,然後再跟甲聊,再跟乙聊。這就叫併發。

二,線程分類

Java線程主要分爲:用戶線程(User Thread)和守護線程(Daemon Thread)

2.1,守護線程

該線程不會單獨執行, 當其他非守護線程(用戶線程)都執行結束後, 自動退出;

設置守護線程的方法:

//設置該線程爲守護線程
thread.setDaemon(true);//注意必須在線程啓動start()方法之前調用。

應用:垃圾回收線程

2.2,用戶線程

主要包括:主線程和子線程(用戶線程),子線程設置了setDaemon(true);就是守護線程;

注意:不能把主線程設置爲守護線程,不然會報IllegalThreadStateException異常;

2.3,區別

用戶線程的優先級高,守護線程的優先級低;

如果JVM中還存在用戶線程,那麼JVM就會一直存活,不會退出;但是JVM中只有守護線程的時候,JVM就會退出;

守護線程依賴於用戶線程,JVM中所有用戶線程退出了,所有的守護線程也就會退出 ;

總之,只要有用戶線程存在,JVM就不會退出;所有用戶線程都退出了,JVM也就退出了,JVM都退出了,守護線程也就退出了;

注意:以下代碼的邏輯讓守護線程提前於用戶線程消亡的情況下,守護線程並不會主動延長生命和用戶線程一起消亡。

public static void main(String[] args) {
	final Thread t = new Thread(){
		public void run() {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
			}
			System.out.println("守護線程");
		};
	};
		
	t.setDaemon(true);
	t.start();
		
	Thread t2 = new Thread(){
		public void run() {
		    for (int i = 0; i < 10; i++) {
			    System.out.println("用戶線程   " + " 守護線程是否存活" +  t.isAlive());
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
				
		};
	};
	t2.start();
}

    
但是讓守護線程延遲於用戶線程消亡的情況下,守護線程會提前和用戶線程一起消亡。

三,多線程實現方式

3.1,通過繼承Thread類,開啓一個線程,實現多線程;

public class ThreadDemo {
	public static void main(String[] args) {
		ThreadTest thTest = new ThreadTest();
		thTest.start();
		
		for (int i = 0; i < 1000; i++) {
			System.out.println("我是主線程");
		}
	}
}

class ThreadTest extends Thread{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		for (int i = 0; i < 1000; i++) {
			System.out.println("子線程");
		}
	}
}

以上通過打印信息可以看到,主線成和子線程是交替執行的,說明有多線程在運行;

3.2,通過實現Runnable接口

public class ThreadDemo2 {
	private void mian() {
		// TODO Auto-generated method stub
		MyRunnable r = new MyRunnable();
		
		Thread t= new Thread(r);
		t.start();
	}
}


class MyRunnable implements Runnable{

	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<1000;i++){
			System.out.println("我是子線程");
		}
	}
	
}

3.3,匿名內部類實現,就是以上兩種方式的另一種寫法;

public class ThreadDemo3 {

	public static void main(String[] args) {
		new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				
				System.out.println("匿名內部類繼承Thread,開啓一個線程");
			}
		}.start();
		
		new Thread(new Runnable() {
			
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("匿名內部類實現Runnable接口,開啓一個線程");
			}
		}).start();
	}
	
}

四,線程常用方法

4.1,獲取線程的名字:getName();

public static void main(String[] args) {
	new Thread(){
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		System.out.println(this.getName() + "   aaaaaa");
	}
    }.start();	
}

4.2,爲線程設置名字,不設置有默認的名稱:setName()

//第一種方式
public static void main(String[] args) {
new Thread(){
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		this.setName("android ");
        System.out.println("當前線程名字:"+this.getName())
	}
}.start();

    //第二種方式,構造方法中爲線程設置一個名字
 new Thread("Windows"){
	@Override
	public void run() {
	    // TODO Auto-generated method stub
		super.run();
        System.out.println("當前線程名字:"+this.getName())
	}
 }.start();
		
}

4.3,獲取當前線程對象:Thread.currentThread

public static void main(String[] args) {
	new Thread(new Runnable() {
	    public void run() {
		    Thread.currentThread().setName("Window");
	    }
    }).start();

    //獲取主線程的名字
    Thread.currentThread.getName();
}

4.4,讓線程休眠的方法:Thread.sleep(休眠時間,單位毫秒);

4.5,join()線程加入方法:讓當前線程暫停, 等待指定的線程執行結束後, 當前線程再繼續執行;

public static void main(String[] args) {
	final Thread t1 = new Thread(){
		public void run() {
			for (int i = 0; i < 10; i++) {
				System.out.println(this.getName()+ " aaaaaaa");
			}	
		};
	};
	Thread t2 = new Thread(){
		public void run() {
			for (int i = 0; i < 10; i++) {
				if(i==2){
					try {
						t1.join();//t1線程加入,t2等待t1執行完成再繼續執行;
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				System.out.println(this.getName()+ " bbbbbbbbbbbbbbbbbb");
			}
		};
	};
	t1.start();
	t2.start();
}

打印輸出:

Thread-0 aaaaaaa
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-0 aaaaaaa
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-0 aaaaaaa
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb
Thread-1 bbbbbbbbbbbbbbbbbb

4.6,setPriority()設置線程優先級

優先級從0(Thread.MIN_PRIORITY)到10(Thread.MAX_PRIORITY)範圍內;

默認優先級:Thread.NORM_PRIORITY=5

4.7,yield() 設置線程禮讓,讓出cpu,讓其他線程執行;

五,線程同步和線程安全

https://blog.csdn.net/ezconn/article/details/100060812

六,線程間通信

6.1,什麼時候需要通信

多個線程併發執行時, 在默認情況下CPU是隨機切換線程的

如果我們希望他們有規律的執行, 就可以使用通信, 例如每個線程執行一次打印

6.2,兩個線程間的通信

通過wait()和notify()方法實現,wait()方法讓當前線程等待,notify()喚醒正在等待的線程;

例如:兩個線程實現循環重複打印兩句詩

實現:

public class ThreadDemo1 {
	
	public static void main(String[] args) {
		final Printer p = new Printer();
		
		new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i =0;
				while (i<1000) {
					i++;
					try {
						p.print1();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
	    }.start();
		new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i =0;
				while (i<1000) {
					i++;
					try {
						p.print2();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		}.start();
		
	}

}

class Printer{
	int flag = 0;
	public synchronized void print1() throws InterruptedException{//同步方法默認的鎖對象是this
		
		if(flag!=0){
			this.wait();//同步鎖對象調用wait()
		}
		
		System.out.print("窗");
		System.out.print("前");
		System.out.print("明");
		System.out.print("月");
		System.out.print("光");
		System.out.print("\r\n");
		
		flag = 2;
		this.notify();//同步鎖對象調用notify()
	}
	public synchronized void print2() throws InterruptedException{//同步方法默認的鎖對象是this
		
		if(flag!=2){
			this.wait(); //同步鎖對象調用wait()
		}
		
		System.out.print("疑");
		System.out.print("是");
		System.out.print("地");
		System.out.print("上");
		System.out.print("霜");
		System.out.print("\r\n");

		flag = 1;
		
		this.notify();//同步鎖對象調用notify()
	}

}

注意:wait()和notify()這兩個方法必須在同步代碼中執行, 並且使用同步鎖對象來調用

6.3,三個或者三個以上的線程通信

通過wait()讓當前線程等待,notifyAll()方法是喚醒所有線程;

public class ThreadDemo2 {
	
	public static void main(String[] args) {
		final Printer2 p = new Printer2();
		
		
		new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i =0;
				while (i<1000) {
					i++;
					try {
						p.print1();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		}.start();
		new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i =0;
				while (i<1000) {
					i++;
					try {
						p.print2();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		}.start();
		new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i =0;
				while (i<1000) {
					i++;
					try {
						p.print3();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		}.start();
		new Thread(){
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i =0;
				while (i<1000) {
					i++;
					try {
						p.print4();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		}.start();
	}

}

class Printer2{
	int flag = 0;
	public synchronized void print1() throws InterruptedException{
		
		while(flag!=0){
			this.wait();
		}
		
		System.out.print("窗");
		System.out.print("前");
		System.out.print("明");
		System.out.print("月");
		System.out.print("光");
		System.out.print("\r\n");
		
		flag = 1;
		this.notifyAll();  //
	}
	public synchronized void print2() throws InterruptedException{
		
		while(flag!=1){
			this.wait();
		}
		
		System.out.print("疑");
		System.out.print("是");
		System.out.print("地");
		System.out.print("上");
		System.out.print("霜");
		System.out.print("\r\n");
		
		flag = 2;
		
		this.notifyAll();
	}
	public synchronized void print3() throws InterruptedException{
		
		while(flag!=2){
			this.wait();
		}
		
		System.out.print("舉");
		System.out.print("頭");
		System.out.print("望");
		System.out.print("明");
		System.out.print("月");
		System.out.print("\r\n");
		
		flag = 3;
		
		this.notifyAll();
	}
	public synchronized void print4() throws InterruptedException{
		
		while(flag!=3){
			this.wait();
		}
		
		System.out.print("低");
		System.out.print("頭");
		System.out.print("思");
		System.out.print("故");
		System.out.print("鄉");
		System.out.print("\r\n");
		
		flag = 0;
		
		this.notifyAll();
	}

}

6.4,JDK1.5之前通過上面的方式,實現多個線程間通信,1.5版本增加了一個互斥鎖ReentrantLock實現;

使用ReentrantLock類中的lock()和unLock()方法替代synchronized實現同步;然後再ReentrantLock創建Condition(監視器)子類對象,每個線程中放一個Condition;通過Condition的await()和signal() 方法實現當前線程等待和喚醒指定的線程;await()相當於Object中的wait()方法;signal()和Object中的notify()和notifyAll()方法有點不一樣;

例如:


public class fThreadDemo3 {

	public static void main(String[] args) {
		final Printer3 p = new Printer3();

		new Thread() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i = 0;
				while (i < 1000) {
					i++;
					try {
						p.print1();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

			}
		}.start();
		new Thread() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i = 0;
				while (i < 1000) {
					i++;
					try {
						p.print2();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

			}
		}.start();
		new Thread() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i = 0;
				while (i < 1000) {
					i++;
					try {
						p.print3();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

			}
		}.start();
		new Thread() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				super.run();
				int i = 0;
				while (i < 1000) {
					i++;
					try {
						p.print4();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

			}
		}.start();
	}

}

class Printer3 {
	int flag = 0;

	ReentrantLock rl = new ReentrantLock();
	Condition c1 = rl.newCondition();
	Condition c2 = rl.newCondition();
	Condition c3 = rl.newCondition();
	Condition c4 = rl.newCondition();

	public void print1() throws InterruptedException {
		rl.lock();
		try {
			if (flag != 0) {
				c1.await();
			}

			System.out.print("窗");
			System.out.print("前");
			System.out.print("明");
			System.out.print("月");
			System.out.print("光");
			System.out.print("\r\n");

			flag = 1;
			c2.signal();
		} finally{
			rl.unlock();
		}
	}

	public void print2() throws InterruptedException {

		rl.lock();
		try {
			if (flag != 1) {
				c2.await();
			}

			System.out.print("疑");
			System.out.print("是");
			System.out.print("地");
			System.out.print("上");
			System.out.print("霜");
			System.out.print("\r\n");

			flag = 2;

			c3.signal();
		} finally {
			rl.unlock();
		}

	}

	public void print3() throws InterruptedException {
		rl.lock();
		try {
			if (flag != 2) {
				c3.await();
			}

			System.out.print("舉");
			System.out.print("頭");
			System.out.print("望");
			System.out.print("明");
			System.out.print("月");
			System.out.print("\r\n");

			flag = 3;

			c4.signal();
		} finally{
			rl.unlock();
		}

	}

	public void print4() throws InterruptedException {
		rl.lock();
		try {
			if (flag != 3) {
				c4.await();
			}

			System.out.print("低");
			System.out.print("頭");
			System.out.print("思");
			System.out.print("故");
			System.out.print("鄉");
			System.out.print("\r\n");

			flag = 0;

			c1.signal();
		} finally {
			rl.unlock();
		}
	}
}

 

 

 

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