創建執行緒的第一種方式:繼承Thread類
-
步驟:
①定義類繼承Thread
②復寫Thread類中的run方法,目的:將自定義代碼存盤在run方法中,讓執行緒運行
③呼叫執行緒的start方法,該方法兩個作用:啟動執行緒,呼叫run方法 -
Thread為什么要覆寫run方法呢?
Thread類用于描述執行緒,該類就定義了一個功能,用于存盤執行緒要運行的代碼,該存盤功能就是run方法,也就是說Thread類中的run方法,用于存盤執行緒要運行的代碼,
代碼示例
class Test_thread extends Thread{
public void run(){
for(int x=0;x<20;x++)
System.out.println(this.getName()+" run---"+x);
}
}
class 執行緒 {
public static void main(String[] args) {
Test_thread t1=new Test_thread();
Test_thread t2=new Test_thread();
t1.start();
t2.start();
for(int x=0;x<20;x++)
System.out.println("main---"+x);
}
}
創建執行緒的第二種方式:實作Runnable介面
-
步驟:
①實作類實作Runnable介面
②覆寫Runnable介面中的run方法,將執行緒要運行的代碼存放在該run方法中
③通過Thread類建立執行緒物件
④將Runnable介面的子類物件作為實際引數傳遞給Thread類的建構式,
⑤呼叫Thread類的start方法開啟執行緒并呼叫Runnable介面子類的run方法 -
為什么要將Runnable介面的子類物件傳遞給Thread的建構式,
因為,自定義的run方法所屬的物件是Runnable介面的子類物件,
所以要讓執行緒去指定指定物件的run方法,就必須明確該run方法所屬物件 -
實作方式和繼承方式有什么區別?
實作方式好處,避免了單繼承的局限性
繼承方式好處,建立使用實作方式 -
兩種方式區別:
執行緒Thread:執行緒代碼存放Thread子類的run方法中
實作Runnable:執行緒代碼存在介面的子類的run方法中
/*
需求:簡單的賣票程式,多個視窗同時買票
*/
class Ticket implements Runnable{//extends Thread{
private int tick=50;
public void run(){
while(true)
if(tick>0)
System.out.println(Thread.currentThread().getName()+"---sale: "+tick--);
}
}
public class thread_example_2 {
public static void main(String[] args) {
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
多執行緒的安全問題
/*
需求:簡單的賣票程式,多個視窗同時買票
*/
class Ticket implements Runnable{//extends Thread{
private int tick=50;
public void run(){
while(true)
if (tick > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() + "---sale: " + tick--);
}
}
}
public class thread_example_2 {
public static void main(String[] args) {
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
通過分析上面代碼,發現,列印出0、-1、-2等錯票,
多執行緒的運行出現了安全問題,
- 問題的原因:
當多條陳述句在操作同一個執行緒共享資料時,一個執行緒對多條陳述句只執行了一部分,還沒有執行完,另一個執行緒參與進來執行,導致共享資料的錯誤, - 解決辦法:
對多條操作共享資料的陳述句,只能讓一個執行緒都執行完,在執行程序中,其他執行緒不可以參與執行,
多執行緒同步代碼塊
Java對于多執行緒的安全問題提供了專業的解決方式,就是同步代碼塊,
synchronized(物件){
需要被同步的代碼
}
- 物件如同鎖,持有鎖的執行緒可以在同步中執行;沒有持有鎖的執行緒即使獲取CPU的執行權,也無法執行
- 同步的前提:
①必須要有兩個或者兩個以上的執行緒;
②必須是多個執行緒使用同一個鎖,必須保證同步中只能有一個執行緒在運行,
好處:解決了多執行緒的安全問題
弊端:多個執行緒需要判斷鎖,較為消耗資源,
/*
需求:簡單的賣票程式,多個視窗同時買票
*/
class Ticket implements Runnable{//extends Thread{
private int tick=50;
Object obj=new Object();
public void run() {
while (true)
synchronized (obj) {
if (tick > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() + "---sale: " + tick--);
}
}
}
}
public class thread_example_2 {
public static void main(String[] args) {
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
多執行緒同步函式
- 如何判斷目標程式是否有安全問題?如果有,如何解決?
①明確哪些代碼是多執行緒運行代碼;
②明確共享資料;
③明確多執行緒運行代碼中那些陳述句是操作共享資料的,
函式需要被物件呼叫,匿名函式都有一個所屬物件參考,就是this,
所以同步函式使用的鎖是this
靜態進記憶體是記憶體中沒有本類物件,但是一定有該類對應的位元組碼檔案物件(類名.class),
所以靜態的同步方法,使用的鎖是該方法所在類的位元組碼檔案物件 類名.class
/*
需求:簡單的賣票程式,多個視窗同時買票
*/
class Ticket implements Runnable{//extends Thread{
private int tick=50;//public static int tick=50;
boolean flag=true;
public void run() {
if(flag) {
while (true)
synchronized (this) {//synchronized(Ticket.class)
if (tick > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() + "---sale: " + tick--);
}
}
}
else
while(true)
show();
}
public synchronized void show(){
if (tick > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() + "---sale: " + tick--);
}
}
}
public class thread_example_2 {
public static void main(String[] args) {
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
try{Thread.sleep(10);}catch(Exception e){}
t.flag=false;
t2.start();
}
}
多執行緒死鎖
代碼示例
class Test implements Runnable{
private boolean flag;
Test(boolean flag){
this.flag=flag;
}
public void run(){
if(flag){
synchronized (Mylock.locka){
System.out.println("if locka");
synchronized (Mylock.lockb) {
System.out.println("if lockb");
}
}
}
else{
synchronized (Mylock.lockb){
System.out.println("else lockb");
synchronized (Mylock.locka) {
System.out.println("else locka");
}
}
}
}
}
class Mylock{
static Object locka=new Object();
static Object lockb=new Object();
}
class DeadTest{
public static void main(String[] args) {
Thread t1=new Thread(new Test(true));
Thread t2=new Thread(new Test(false));
t1.start();
t2.start();
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/239626.html
標籤:其他
