一.主線程是負責Java程序執行,它存在與mian方法中
* jvm啓動不止一個線程,還有負責垃圾回收機制的線程
*
* Thread類是java對線程的封裝,API中說明只需要繼承這個類就能實現一個線程。
*
* 創建線程的第一種方式,就是繼承Thread類
* 步驟:1.定義類繼承Thread
* 2.重寫Thread類中的run方法
* 目的:將自定義的代碼存儲到run方法中,讓線程運行
*
* 3.調用線程的start方法(作用:啓動線程,調用run方法)
創建線程的第二種方式:實現Runnable接口
步驟:
1,定義類實現Runnable接口
2,覆蓋Runnable接口中的run方法
將線程要運行的代碼放到該run方法中
3,通過Thread類建立線程對象
4,將Runnable接口的子類對象作爲實際參數傳遞給Thread類的構造函數(爲了放線程去指定對象的run方法)
5,調用Thread類的start方法開啓線程,並調用Runnable接口子類的run方法
實現方式和繼承方式有什麼區別?
實現方式好處:避免了單繼承的侷限性,我們建議使用實現方式進行定義線程。
區別: 繼承Thread:線程代碼存放在Thread子類的run方法中
實現Runnable:線程代碼存放在接口的子類的run方法中。
* 通過結果:運行的結果每次都是不同的。
* 因爲多個線程都在獲取cpu的執行權,cpu執行到誰,就運行誰。
*
* 結論:cpu執行過程中的某一時刻,只能有一個程序在運行(多核除外)
* 執行時間由cpu決定。
*
* 多線程的特性:隨機性。
*
* 常用方法:static Thread currentThread()獲取當前線程對象
* getName();獲取線程名稱
*
* 設置線程名稱:setName或者構造函數
多線程安全問題:常見問題,當多條語句在操作同一個線程共享數據是,一個線程對多條語句只執行了一部分
還沒執行完,另外一個線程參與進來執行,導致共享數據的錯誤。
解決辦法:
對多條操作共享數據的語句,只能讓一個線程都執行萬,在執行過程中,其他線程不可以參與。
Java對於多線程的安全問題提供了專業的解決方式(同步代碼):
synchronized(對象){
需要同步的代碼;
}
對象如同鎖,持有鎖的線程可以在同步中執行,沒有鎖的線程即使得到cpu執行權,也進不去,因爲沒有獲取鎖
如何停止線程?
方法:只有一種方法,run方法結束。(stop方法已經過時)
開啓多線程運行,運行代碼通常是循環結構
要讓線程結束只有控制循環
特殊情況:
當線程處於凍結狀態
就不會讀取到標記,那麼線程就不會結束
例子1:
class Demo extends Thread {
public Demo(String name) {
super(name); //用於標識線程的名稱
}
public void run() {
for (int i = 0; i <= 60; i++) {
System.out.println(Thread.currentThread().getName()+"Demo run " + i);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
Demo d = new Demo("one-----"); //創建一個進程
Demo b = new Demo("two-----"); //創建一個進程
d.start();//開啓線程並執行該線程的run方法
b.start();
// d.run(); 僅僅是對象調用方法,而線程創建了,並沒有使用
for (int i = 0; i <= 60; i++) {
System.out.println("Mian run " + i);
}
}
}
同步的前提:
1,必須要有兩個或者兩個以上的線程。
2,必須是多個線程使用同一個鎖
同步好處:解決了多線程的安全問題
同步弊端:多個線程需要判斷鎖,比較消耗資源
例子2:
需求:一個售票小程序
class Ticket implements Runnable {
private static int tick = 100;
Object obj = new Object();
@Override
public void run() {
while (true) { // 注意 這裏會把資源耗盡的 小心哦
synchronized (obj) {
if (tick > 0) {
try {
Thread.sleep(10); // 休眠10毫秒
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName()
+ "sale*********" + tick--);
}
}
}
}
}
public class TicketDemo {
/**
* @param args
*/
public static void main(String[] args) {
// 開啓四個售票窗口
Ticket tick = new Ticket();
Thread t1 = new Thread(tick);
Thread t2 = new Thread(tick);
Thread t3 = new Thread(tick);
Thread t4 = new Thread(tick);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
例子3:
/*
需求:銀行存錢,有兩個用戶分別存入300元,每次存入100,存3次
多線程安全:多線程使用共享數據時,必須使用同步鎖,防止出現安全問題(synchronized)
靜態的同步方法:使用的鎖是該方法所在類的字節碼文件對象。類名.class
*/
class Bank {
private int sum;
public synchronized void add(int n) { //同步,使得多線程安全
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
sum = sum + n;
System.out.println(Thread.currentThread().getName() + "---->sum:" + sum);
}
}
class Cus implements Runnable{
private Bank b = new Bank();
@Override
public void run() {
for(int i =0 ; i<3 ;i++){
b.add(100);
}
}
}
public class BankDemo {
public static void main(String[] args) {
Cus c = new Cus();
//添加兩個線程
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
例子4:
/*
* 需求:兩個用戶,一個往數據庫中加入數據,一個向數據庫中申請讀出數據
*
* 多線程通訊:要解決線程安全問題,使用到等待喚醒機制
*
* wait();
* notify();
* notifyall();
* 都是使用在同步中,因爲要對持有監視器(鎖)的線程操作
* 所以要使用在同步中,因爲持有同步才具有鎖
*
* 等待和喚醒必須是同一個鎖
* 而鎖可以是任意對象,所有可以被任意對象調用的方法定義Object類中
*/
class Res{
String name;
String sex;
boolean flag = false;
}
class Input implements Runnable{
private Res r;
public Input(Res r) {
this.r = r;
}
@Override
public void run() {
int x= 0;
while (true) {
synchronized (r) {
if(r.flag)
try {
r.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(x == 0)
{
r.name="make";
r.sex="man";
}
else{
r.name="麗麗";
r.sex="女女女女女女";
}
x=(x+1)%2;
r.flag=true;
r.notify();
}
}
}
}
class Output implements Runnable{
private Res r;
Output(Res r){
this.r= r;
}
public void run(){
while (true) {
synchronized (r) {
if(!r.flag)
try {
r.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(r.name + "........." + r.sex);
r.flag=false;
r.notify();
}
}
}
}
public class InputOutputDemo {
/**
* @param args
*/
public static void main(String[] args) {
Res r = new Res();
Input in = new Input(r);
Output out = new Output(r);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
例子5:
守護線程:當主線程執行完畢時,只剩下守護線程是,java 虛擬機會自動退出。
class StopThread implements Runnable {
private boolean flag = true;
@Override
public synchronized void run() {
while (flag) {
try {
this.wait();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()
+ "++++Exception-----");
flag = false;
}
System.out.println(Thread.currentThread().getName() + "run------");
}
}
public void changeFlag() {
flag = false;
}
}
public class StopThreadDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
StopThread s = new StopThread();
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
t1.setDaemon(true); //標記線程爲守護線程,必須在啓動線程之前調用
t2.setDaemon(true);
t1.start();
t2.start();
int num = 0;
while (true) {
if (num++ == 60) {
// s.changeFlag();
t1.currentThread(); // 線程中斷:就是使處在凍結狀態的線程回到運行狀態
t2.currentThread();
break;
}
System.out.println(Thread.currentThread().getName() + "--main-----"+num);
}
System.out.println("over");
}
}