【Java系列】【基礎版】多線程基礎

1.


多線程基礎

1.1 認識進程和線程

1.1.1 什麼是進程

    1.1.1.1 進程是正在進項的程序,是資源分配的一個基本單位,有內存分配;

1.1.2 什麼是線程

    1.1.2.1 線程是進程的一個執行單位,也是進程的執行順序; 

    1.1.2.2 一個進程至少有一個線程,可以由兩個或以上的線程;

1.1.3 JVM至少有幾個線程

    1.1.3.1 至少有一個或兩個線程,main方法和垃圾回收線程;

1.1.4 什麼是多線程

    1.1.4.1 2個或以上的線程去執行

1.1.5 多線程的作用

    1.1.5.1 提高效率,線程之間切換的小號是可以接受的;

    1.1.5.2 單線程容易阻塞;

    1.1.5.3 多個線程同時執行代碼;

    1.1.5.4 一個線程掛了,還有其他的線程在,程序還在執行

1.1.6 進程和線程是誰創建的

    都是操作系統創建的

1.1.7 如何自定義一個線程

    1.1.7.1 繼承Thread類,重寫run方法

    1.1.7.2 生成對象

    1.1.7.3 開啓start

1.1.8 所有實現的接口

    1.1.8.1 Runnable接口

    1.1.8.2 new線程對象的時候,創建了線程

    1.1.8.3 start方法,溝通了操作系統,告訴操作系統有一個線程要開啓,也調用了run


1.2 代碼示例

1.2.1 在main主線程裏,再度開啓其他線程

1.2.1.1 測試類:

public class Test {

public static void main(String[] args) throws ParseException  {

Test001 t = new Test001();//這裏代表着產生了一個線程

Test001 t1 = new Test001();//這裏代表着產生了一個線程

System.out.println("開啓線程之前");

//t1.run();//代表着調用了對象的方法

t.start();//這裏代表開啓了一個線程,是溝通了操作系統

t1.start();//這裏代表開啓了一個線程,是溝通了操作系統

try {

Thread.sleep(15);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("開啓線程之後");

System.out.println("main  over");

}

}

1.2.1.2 重寫run方法類,繼承Thread類:

class Test001 extends Thread{

@Override

public void run() {

for (int i = 0; i < 100; i++) {

try {

sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("run    .. "+i);

}

}

}



1.2.2 創建一個線程,和主程序交替執行

1.2.2.1 測試類

public class ThreadTest {

public static void main(String[] args) {

Test t = new Test();

t.start();

for (int i = 0; i < 10; i++) {

System.out.println("main>>>"+i);

}

System.out.println("main  -  gg");

}

}

1.2.2.2 方法類

class Test extends Thread{

int i = 0;

@Override

public void run() {

while(true){

if(i<10){

System.out.println("run方法..." + i++);

}else{

System.out.println("run  - OVER");

break;

}

}

}

}


1.2.3 模擬買票功能

1.2.3.1 測試類

public class TestRunnable01 {

public static void main(String[] args) {

Runnable x1 = new XinXiaoMi();

Thread t1 = new Thread(x1);

Thread t2 = new Thread(x1);

Thread t3 = new Thread(x1);

t1.setName("小紅");

t2.setName("小藍");

t3.setName("小綠");

t1.start();

t2.start();

t3.start();

}

}

1.2.3.2 方法類

class XinXiaoMi implements Runnable{

static int i =100;//共享的

Object o = new Object();

@Override

public void run() {

boolean b = true;

while(b){

synchronized (o) {

if(i>0){//i = 1

try {

Thread.sleep(10);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+"...賣出了手機,編號是..." + i--);

}

else{

break;

}

}

}

}

}


1.2.4 小明和老婆同時存錢,每次存取500元,每人存5次

1.2.4.1 測試類:

public class TestRunnable02 {

public static void main(String[] args) {

test t = new test();

Thread t1 = new Thread(t);

Thread t2 = new Thread(t);

t1.setName("小明");

t2.setName("陳學冬");

t1.start();

t2.start();

}

}

1.2.4.2 方法類:

class test implements Runnable{

int con = 0;//初始定義了賬戶餘額爲0元

Object o = new Object();

@Override

public void run() {

synchronized (o) {

for (int i = 0; i < 5; i++) {

con += 500;

try {

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+"  存了500大洋---此時賬戶餘額爲  。。"+con);

}

}

}

}


1.2.4 小明和老婆同時存錢,每次存取500元,每人存5次; 同時實現方法的同步

1.2.4.1 測試類

public class TestM {

public static void main(String[] args) {

test00 t = new test00();

Thread t1 = new Thread(t);

Thread t2 = new Thread(t);

t1.setName("小明");

t2.setName("baby");

t2.start();

t1.start();

}

}

1.2.4.2 方法類

class test00 implements Runnable{

int con = 0;//初始定義了賬戶餘額爲0元

Object o = new Object();

@Override

public void run() {

System.out.println(Thread.currentThread().getName()+" 到達房間門口");

this.show();//一次就好,許你地老天荒

}

public synchronized void show(){

System.out.println(Thread.currentThread().getName()+" 鎖住了房間");

for (int i = 0; i < 5; i++) {

con += 500;

try {

Thread.sleep(10);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+"  存入了500,此時餘額是    "+con);

}

System.out.println(Thread.currentThread().getName()+" 離開了房間,打開了鎖");

}

}


1.2.5 小明和老婆同時存錢,每次存取500元,每人存5次; 同時實現方法的同步;同時實現交替存儲

1.2.5.1 測試類

public class TestM2 {

public static void main(String[] args) {

test000 t = new test000();

Thread t1 = new Thread(t);

Thread t2 = new Thread(t);

t1.setName("小明");

t2.setName("baby");

t2.start();

t1.start();

}

}

1.2.5.2 方法類

class test000 implements Runnable{

int con = 0;//初始定義了賬戶餘額爲0元

Object o = new Object();

@Override

public void run() {

for (int i = 0; i < 5; i++) {

//等待區

show();

try {

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

public synchronized void show(){

System.out.println(Thread.currentThread().getName()+" 上鎖了");

con += 500;

System.out.println(Thread.currentThread().getName()+"  存入了  500 ,  此時餘額爲   "+con);

System.out.println(Thread.currentThread().getName()+" 鎖開了");

}

}


1.2.6 死鎖的示例

1.2.6.1 測試類:

 /* Runnablere =new Runnable() {

public void run() {

}

};

 */

public class Test01 {

public static void main(String[] args) {

TestSY ts1 = new TestSY();

Thread th1 = new Thread(ts1);

Thread th2= new Thread(ts1);

th1.start();

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

TestSY.b = false;

th2.start();

}

}


1.2.6.2 方法類:


class TestSY implements Runnable{

static boolean b = true;

Object o = new Object();

Object o1 = new Object();

@Override

public void run() {

if( b ){

synchronized (o) {

System.out.println("if  拿到了 o 的鎖");

try {

Thread.sleep(10000);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized (o1) {

System.out.println("if-----");

}

}

}else{

synchronized (o1) {

System.out.println("else  拿到了 o1 的鎖");

try {

Thread.sleep(10000);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized (o) {

System.out.println("else-----");

}

}

}

    }

}


1.3 cpu調度

    cpu的時間片,cpu控制着執行;

    cpu會自行分配資源給線程,得到資源的線程可以執行,沒有得到資源的線程沒有辦法執行;

    多個cpu纔是真正意義上的同時執行多線程,因爲運行速度特別多,會給我們產生錯覺,認爲是同時執行的;


1.4 線程的狀態

1.4.1 新生

    1.4.1.1 new對象的時候代表着新生;

1.4.2 就緒

    1.4.2.1 start方法代表着就緒,這個時候在等待着CPU的資源;如果有資源就進入執行狀態,沒有資源就繼續等待;

1.4.3 執行

    1.4.3.1 拿到時間片,正在執行;時間片被拿走了就離開了執行狀態;

1.4.4 阻塞

    1.4.4.1 睡覺sleep,讓資源;時間結束了就回到就緒狀態,等待資源;

1.4.5 死亡

    1.4.5.1 線程執行結束;非正常死亡方式;


1.5 兩種創建線程方式的比較

1.5.1 繼承方式創建簡單,使用的方法也多;

1.5.2 java是單繼承的,使用集成方式過於死板;

1.5.3 繼承是不共享資源的,實現方式是共享的;

1.5.4 主要由於單繼承的機制,推薦使用Runnable接口實現這種方式

    1.5.4.1 實現接口,重寫方法

    1.5.4.2 new一個對象

    1.5.4.3 把new的對象放進線程(Thread)對象裏

    1.5.4.4 用線程對象開啓線程

    1.5.4.5 注意:run方法不能拋異常


1.6 同步代碼塊原理解析

    1.6.1 java提供了鎖的機制synchronized ,可以鎖住一段代碼,在任何時期這段代碼裏面最多隻有一個線程在執行。每個要進來的線程都會先判斷此處有沒有上鎖。

1.6.1.1 如果沒有,就進去並且上鎖,再執行完之後纔會離開,同時打開鎖,其他的線程纔可以進來

1.6.1.2 如果被鎖,再門口等待,進不去; 這裏鎖,鎖的是對象。具體鎖那個對象呢。具體情況具體分析

    1.6.2 同步的優缺點

        1.6.2.1 同步的前提

            1.6.2.1.1 兩個或兩個以上的線程去操作共享數據時

        1.6.2.2 同步的好處

            1.6.2.2.1 保證了線程的安全性

        1.6.2.3 同步的缺陷

            1.6.2.3.1 消耗增加,但是影響不大

            1.6.2.3.2 使用不當容易造成死鎖


===========================================================================================



1.7 線程之間的通訊

1.7.1 生產者消費者阻塞隊列

1.7.1.1 阻塞隊列(BlockingQueue)

1.7.1.1.1 棧和隊列是在程序中被廣泛使用的兩種線性數據結構

1.7.1.1.2 Java5提供了阻塞隊列的接口BlockingQueue,阻塞隊列的概念是:一個指定長度的隊列,如果隊列滿了,添加新元素的操作會被阻塞等待,直到有空位爲止;同時,當隊列爲空的時候,請求隊列元素的操作同樣會阻塞等待,直到有可用元素爲止;

1.7.1.1.3 Java阻塞隊列應用於生產者消費者模式、消息傳遞、並行任務執行和相關併發設計的大多數常見使用上下文; 程序的兩個線程通過交替向BlockingQueue中放入元素、取出元素,即可很好的控制線程的通信;

1.7.1.1.4 BlockingQueue提供了兩個支持阻塞的方法:

    1.7.1.1.4.1 put(E e), 嘗試把E元素放入BlockingQueue中,如果該隊列的元素已滿,則阻塞該線程;

     1.7.1.1.4.1 take( ), 嘗試從BlockingQueue的頭部取出元素,如果該隊列的元素爲空,則阻塞該線程;

1.7.1.1.5  






































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