## Java多線程
### 進程和線程基本概念
### 進程--概念
????進程是指運行中的應用程序,每個進程都有自己獨立的地址空間(內存空間),比如用戶打開一個軟件,這就相當于啟動了一個進程,操作系統就會為該進程分配獨立的地址空間。當用戶打開另一個軟件,相當于又啟動了一個進程,操作系統將為新的進程分配新的獨立的地址空間。目前操作系統都支持多進程。
要點:用戶每啟動一個進程,操作系統就會為該進程分配一個獨立的內存空間。
?
### 線程--概念
????線程是進程中的一個實體,是被系統獨立調度和分派的基本單位,線程自己不擁有系統資源,只擁有一點在運行中必不可少的資源,但它可與同屬一個進程的其它線程共享進程所擁有的全部資源。一個線程可以創建和撤消另一個線程,同一進程中的多個線程之間可以并發執行。線程有就緒、阻塞和運行三種基本狀態。
線程
1、線程是輕量級的進程
2、線程沒有獨立的地址空間(內存空間)
3、線程是由進程創建的(寄生在進程)
4、一個進程可以擁有多個線程-->這就是我們常說的多線程編程
5、線程有幾種狀態:
??a、新建狀態(new)
?b、就緒狀態(Runnable)
??c、運行狀態(Running)
??d、阻塞狀態(Blocked)
?e、死亡狀態(Dead)

### 線程的用處
解決了并發問題,也就是說當對象需要同時運行時,就可以用多線程技術。
### 線程--如何使用
在java中一個類要當作線程來使用有兩種方法。
### 1、繼承Thread類,并重寫run函數
### 2、實現Runnable接口,并重寫run函數
因為java是單繼承的,在某些情況下一個類可能已經繼承了某個父類,這時在用繼承Thread類方法來創建線程顯然不可能,java設計者們提供了另外一個方式創建線程,就是通過實現Runnable接口來創建線程。
### 通過繼承Thread類和實現Runnable接口來實現建立線程
~~~
/**
* 繼承Thread類實現線程
* 或者實現Runnable接口
*/
package com.thread;
public class Thread1 {
public static void main(String[] args) {
// 創建對象
Cat cat = new Cat();
// 啟動線程,調用run()方法
cat.start();
Dog dog = new Dog();
//創建線程
Thread t = new Thread(dog);
//啟動線程
t.start();
}
}
class Cat extends Thread{
// 記數
int times = 0;
public void run() {
// 休眠一秒
// 1000毫秒 = 1秒
// sleep()進入Block狀態,并釋放資源
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
times++;
System.out.println("hello,world " + times);
if (times == 10) {
break;
}
}
}
}
// 創建Runnable接口
class Dog implements Runnable {
int times = 0;
// 重寫run函數
public void run() {
// 休眠一秒
// 1000毫秒 = 1秒
// sleep()進入Block狀態,并釋放資源
while (true) {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
times++;
System.out.println("hello,wrold!" + times);
if (times == 10) {
break;
}
}
}
}
~~~
~~~
/**
* 兩個線程同時運行的案例
*/
package com.thread;
public class Thread2 {
public static void main(String[] args) {
Pig pig = new Pig(10);
Bird bird = new Bird(10);
//創建線程
Thread t1 = new Thread(pig);
Thread t2 = new Thread(bird);
//啟動線程
t1.start();
t2.start();
}
}
//累加
class Bird implements Runnable{
int n = 0;
int times = 0;
int sum = 0;
public Bird(int n){
this.n = n;
}
public void run(){
while(true){
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
sum += (++times);
System.out.println("當前結果是:" + sum);
if(times == n){
System.out.println("最后結果是:" + sum);
break;
}
}
}
}
//打印
class Pig implements Runnable{
int n = 0;
int times = 0;
public Pig(int n){
this.n = n;
}
public void run(){
while(true){
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
times++;
System.out.println("線程輸出 " + times);
if(times == n){
break;
}
}
}
}
~~~
### 線程--繼承Thread?和實現Runnable的區別
? ? 從java的設計來看,通過繼承Thread或者實現Runnable接口來創建線程本質上沒有區別,從jdk幫助文檔我們可以看到Thread類本身就實現了Runnable接口,如果一定要說它們有什么區別,有如下幾點:
1、盡可能使用實現Runnable接口的方式來創建線程
2、在使用Thread的時候只需要new一個實例出來,調用start()方法即可以啟動一個線程,如:
~~~
Thread test=new Thread();
test.start();
~~~
3、在使用Runnable的時候需要先new一個實現Runnable的實例,之后用Thread調用,如:
~~~
Test t=new Test();//Test實現了Runnable接口
Thread test=new Thread(t);
tset.start();
~~~
用實現Runnable接口的特點
1、用實現Runnable接口的方法創建對象可以避免java單繼承機制帶來的局限;
2、用實現Runnable接口的方法,可以實現多個線程共享同一段代碼(數據);
因此如果程序有同步邏輯需求,則使用Runnable的方法來創建線程。
?
?
### 線程--深入理解
不管是通過繼承Thread,還是通過實現Runnable接口創建線程,它們的一個對象只能啟動(即:start())一次。否則就會有異常拋出。
### java線程的同步
### 線程同步機制--介紹
多線程編程為我們處理同時并發的事務提供了很好的幫助,但是由此而來卻引發一個嚴重的問題,那就是線程的安全問題。
而解決這個問題的關鍵,是保證事務的原子性(線程1開始執行某段原子代碼,線程2要執行這段代碼,必須等待線程1執行完成后,才能執行。)
Java中處理事務原子性的方法非常簡單,只需要在需要保證原子性的代碼塊用如下代碼:
~~~
synchronized(Object){
<span style="white-space:pre"> </span>代碼塊
}
~~~
這就是java線程的同步機制。
~~~
package com.thread;
public class Thread3 {
public static void main(String[] args) {
//定義售票窗口
TicketWindow tw1 = new TicketWindow();
//創建線程
Thread t1 = new Thread(tw1);
Thread t2 = new Thread(tw1);
Thread t3 = new Thread(tw1);
//啟動線程
t1.start();
t2.start();
t3.start();
}
}
//售票窗口類
class TicketWindow implements Runnable {
//票數
private int nums = 2000;
@Override
public void run() {
while(true){
//同步代碼塊
synchronized (this) {
// 判斷是否有票
if (nums > 0) {
// 顯示售票信息,currentThread().getName()獲取當前線程的名字
System.out.println(Thread.currentThread().getName() + "正在售出第" + nums + "票");
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
nums--;
} else {
// 票已賣完
break;
}
}
}
}
}
~~~
###
### 線程同步機制--深入理解


java任意類型的對象都有一個標志位,該標志位具有0、1兩種狀態,其開始狀態為1,當某個線程執行了synchronized(Object)語句后,object對象的標志位變為0的狀態,直到執行完整個synchronized語句中的代碼塊后,該對象的標志位又回到1狀態。
當一個線程執行到synchronized(Object)語句的時候,先檢查Object對象的標志位,如果為0狀態,表明已經有另外的線程正在執行synchronized包括的代碼,那么這個線程將暫時阻塞,讓出CPU資源,直到另外的線程執行完相關的同步代碼,并將Object對象的標志位變為1狀態,這個線程的阻塞就被取消,線程能繼續運行,該線程又將Object的標志位變為0狀態,防止其它的線程再進入相關的同步代碼塊中。
????如果有多個線程因等待同一個對象的標志位面而處于阻塞狀態時,當該對象的標志位恢復到1狀態時,只會有一個線程能夠進入同步代碼執行,其它的線程仍處于阻塞的狀態。
?
?
----------參考《韓順平.循序漸進學.java.從入門到精通》
----------參考《JDK_API_1_6_zh_CN》
Java學習筆記--導航[http://blog.csdn.net/q547550831/article/details/49819641](http://blog.csdn.net/q547550831/article/details/49819641)