【Java系列】【基礎版】多線程基礎
多線程基礎
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 使用不當容易造成死鎖
【Java系列】【基礎版】多線程基礎