Java實(shí)現(xiàn)模擬機(jī)器人對(duì)話的示例代碼
前言
今天帶大家來體驗(yàn)一下Java多線程,首先我們要明白什么是線程?什么是多線程?
進(jìn)程是指一個(gè)內(nèi)存中運(yùn)行的應(yīng)用程序,每個(gè)進(jìn)程都有自己獨(dú)立的一塊內(nèi)存空間,一個(gè)進(jìn)程中可以啟動(dòng)多個(gè)線程。比如在Windows系統(tǒng)中,一個(gè)運(yùn)行的exe就是一個(gè)進(jìn)程。
線程是指進(jìn)程中的一個(gè)執(zhí)行流程,一個(gè)進(jìn)程可以運(yùn)行多個(gè)線程。比如java.exe進(jìn)程可以運(yùn)行很多線程。線程總是輸入某個(gè)進(jìn)程,進(jìn)程中的多個(gè)線程共享進(jìn)程的內(nèi)存。
多線程指的是這個(gè)程序(一個(gè)進(jìn)程)運(yùn)行時(shí)產(chǎn)生了不止一個(gè)線程。
一、Java多線程的介紹
我們知道,Java編寫程序都運(yùn)行在在Java虛擬機(jī)(JVM)中,在JVM的內(nèi)部,程序的多任務(wù)是通過線程來實(shí)現(xiàn)的。每用java命令啟動(dòng)一個(gè)java應(yīng)用程序,就會(huì)啟動(dòng)一個(gè)JVM進(jìn)程。在同一個(gè)JVM進(jìn)程中,有且只有一個(gè)進(jìn)程,就是它自己。在這個(gè)JVM環(huán)境中,所有程序代碼的運(yùn)行都是以線程來運(yùn)行。
一般常見的Java應(yīng)用程序都是單線程的。比如,用java命令運(yùn)行一個(gè)最簡(jiǎn)單的HelloWorld的Java應(yīng)用程序時(shí),就啟動(dòng)了一個(gè)JVM進(jìn) 程,JVM找到程序程序的入口點(diǎn)main(),然后運(yùn)行main()方法,這樣就產(chǎn)生了一個(gè)線程,這個(gè)線程稱之為主線程。當(dāng)main方法結(jié)束后,主線程運(yùn)行完成。JVM進(jìn)程也隨即退出 。
對(duì)于一個(gè)進(jìn)程中的多個(gè)線程來說,多個(gè)線程共享進(jìn)程的內(nèi)存塊,當(dāng)有新的線程產(chǎn)生的時(shí)候,操作系統(tǒng)不分配新的內(nèi)存,而是讓新線程共享原有的進(jìn)程塊的內(nèi)存。因此,線程間的通信很容易,速度也很快。不同的進(jìn)程因?yàn)樘幱诓煌膬?nèi)存塊,因此進(jìn)程之間的通信相對(duì)困難。
多線程能滿足程序員編寫高效率的程序來達(dá)到充分利用 CPU 的目的。
線程是一個(gè)動(dòng)態(tài)執(zhí)行的過程,它也有一個(gè)從產(chǎn)生到死亡的過程。
下圖顯示了一個(gè)線程完整的生命周期:
二、創(chuàng)建線程并運(yùn)行
接下來,我們使用Thread創(chuàng)建一個(gè)線程并運(yùn)行:
1.打開Idea新建工程,再右擊src新建包Thread,在包上點(diǎn)右鍵,創(chuàng)建一個(gè)PeopleA類,輸入代碼:
package Thread; public class PeopleA extends Thread{ @Override public void run() { //被調(diào)用start方法后,就會(huì)執(zhí)行run方法里的代碼 System.out.println(this.getName() + " 線程開始執(zhí)行了:"); try { Thread.sleep(5000); //休眠5秒,模擬子線程需要5秒才能完成任務(wù)。 } catch (InterruptedException e) { e.printStackTrace();//在命令行打印異常信息在程序中出錯(cuò)的位置及原因。 } System.out.println(this.getName() + " 線程執(zhí)行結(jié)束了:"); } }
2.再右擊Thread包創(chuàng)建一個(gè)ThreadTest類,并輸入代碼:
package Thread; public class ThreadTest { public static void main(String[] args) { //在mian 線程(主線程)中創(chuàng)建了一個(gè)子線程 peopleA PeopleA peopleA = new PeopleA(); //給子線程取個(gè)名字:A線程 peopleA.setName("A線程"); //啟動(dòng)peopleA線程,啟動(dòng)后系統(tǒng)將增加一個(gè)線程去執(zhí)行PeopleA中的run方法里的代碼 peopleA.start(); //打印這句表示主線程啟動(dòng)子線程后,會(huì)繼續(xù)執(zhí)行后續(xù)代碼,不會(huì)關(guān)心子線程什么時(shí)候執(zhí)行 System.out.println("main函數(shù)結(jié)束了。"); } }
3.運(yùn)行,查看結(jié)果,體會(huì)多線程的執(zhí)行:
主線程啟動(dòng)子線程后,會(huì)繼續(xù)執(zhí)行后續(xù)代碼,不會(huì)關(guān)心子線程什么時(shí)候執(zhí)行。
這里還需注意,主線程的代碼執(zhí)行完畢后,整個(gè)程序并沒有立即結(jié)束運(yùn)行,而是等待子線程運(yùn)行完后再結(jié)束運(yùn)行并回收資源。
三、多線程間的交互
①模擬兩個(gè)機(jī)器人的對(duì)話
1.我們右擊src,新建一個(gè)包c(diǎn)om.my.thread,并右擊創(chuàng)建名為L(zhǎng)anguage的對(duì)象,用來存儲(chǔ)問題和答案,用于2個(gè)機(jī)器人的交互。代碼如下:
package com.my.thread; import java.util.Random; //預(yù)先設(shè)定好可能的對(duì)話內(nèi)容 public class Language { //問題集合 static final String[] questions = { "你叫什么名字?", "現(xiàn)在幾點(diǎn)了?", "你早飯吃的什么?", "你中午吃的什么?", "你晚餐吃的什么?", "你的身高多少?", "你最喜歡的Csdn博主是誰?" }; //隨機(jī)數(shù)生成器 static Random random = new Random(); //當(dāng)前問題 static String question = null; //當(dāng)前答案 static String answer = null; /**隨機(jī)獲取一個(gè)問題*/ public static String getARandomQuestion() { int index = random.nextInt(questions.length); return questions[index]; } //設(shè)置當(dāng)前答案 public static void setAnswer(String answer) { Language.answer = answer; } //設(shè)置當(dāng)前問題 public static void setQuestion(String question) { Language.question = question; } }
2. 在com.my.thread包上創(chuàng)建名為PeopleA的對(duì)象,用來模擬提問者。代碼如下:
package com.my.thread; public class PeopleA extends Thread{ @Override public void run() { //被調(diào)用start方法后,就會(huì)執(zhí)行run方法里的代碼 System.out.println(this.getName() + " 我要開始提問了。"); //使用死循環(huán)寫法,讓線程PeopleA永遠(yuǎn)不會(huì)自動(dòng)停止運(yùn)行 while(true){ //沒有人回答問題 并且沒有人提問的時(shí)候,就提一個(gè)問題 if(Language.question == null) { String q = Language.getARandomQuestion();//獲取一個(gè)隨機(jī)問題 Language.setQuestion(q);//設(shè)置問題 System.out.println(this.getName() + ":" + q); Language.setAnswer(null);//提出問題后,把答案設(shè)置為空 }else{ System.out.println("請(qǐng)回答我的問題..."); } try { //隨機(jī)休眠0-15秒,模擬用戶A的思考時(shí)間 Thread.sleep(1000 * Language.random.nextInt(15)); } catch (InterruptedException e) { e.printStackTrace(); } } } }
3. 在com.my.thread包上創(chuàng)建名為PeopleB的對(duì)象,用來模擬回答者。代碼如下:
package com.my.thread; import java.text.SimpleDateFormat; import java.util.Date; public class PeopleB extends Thread{ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Override public void run() { //被調(diào)用start方法后,就會(huì)執(zhí)行run方法里的代碼 System.out.println(this.getName() + " 我準(zhǔn)備好回答了。"); //使用死循環(huán)寫法,讓線程PeopleB永遠(yuǎn)不會(huì)自動(dòng)停止運(yùn)行 while(true){ //有人提問,且沒有人回答的情況下,就回答問題 if(Language.answer == null) { String an = answerQuestion(Language.question); //根據(jù)問題得到答案 Language.setAnswer(an);//設(shè)置答案 System.out.println(this.getName() + ":" + an);//打印答案 Language.question = null;//回答完畢后,把問題設(shè)置為空。 }else{ System.out.println("請(qǐng)你繼續(xù)提問..."); } try { //隨機(jī)休眠0-15秒,模擬用戶B的思考時(shí)間 Thread.sleep(1000 * Language.random.nextInt(15)); } catch (InterruptedException e) { e.printStackTrace(); } } } //根據(jù)問題得到答案 private String answerQuestion(String question) { if(question == null){ return "請(qǐng)開始提問..."; } String an = "這個(gè)問題太難了,答不上來。";//默認(rèn)回答語句 if(question.equals("你叫什么名字?")){ an = this.getName(); }else if(question.equals("現(xiàn)在幾點(diǎn)了?")){ an = format.format(new Date()); }else if(question.equals("你早飯吃的什么?")){ an = "豆?jié){油條。"; }else if(question.equals("你的身高多少?")){ an = "身高1米85。"; }else if(question.equals("你最喜歡的Csdn博主是誰?")){ an = "青00(一起學(xué)Java)"; }else{ } return an; } }
4.創(chuàng)建ThreadTest對(duì)象的main函數(shù),啟動(dòng)2個(gè)線程,觀察運(yùn)行結(jié)果(每次結(jié)果可能都不一樣):
package com.my.thread; public class ThreadTest { public static void main(String[] args) { //在mian 線程(主線程)中創(chuàng)建了2個(gè)子線程 peopleA,peopleB PeopleA peopleA = new PeopleA(); PeopleB peopleB = new PeopleB(); //給子線程取個(gè)名字 peopleA.setName("提問者"); peopleB.setName("回答者"); //啟動(dòng)peopleA,peopleB線程,啟動(dòng)后系統(tǒng)將增加一個(gè)線程去執(zhí)行run方法里的代碼 peopleA.start(); peopleB.start(); //打印這句表示主線程啟動(dòng)子線程后,會(huì)繼續(xù)執(zhí)行后續(xù)代碼,不會(huì)關(guān)心子線程什么時(shí)候執(zhí)行 System.out.println("main函數(shù)結(jié)束了。"); } }
5.運(yùn)行ThreadTest.java,觀察結(jié)果,體會(huì)多線程的工作原理。
多線程的使用注意事項(xiàng):有效利用多線程的關(guān)鍵是理解程序是并發(fā)執(zhí)行而不是串行執(zhí)行的。
例如:程序中有兩個(gè)子系統(tǒng)需要并發(fā)執(zhí)行,這時(shí)候就需要利用多線程編程。通過對(duì)多線程的使用,可以編寫出非常高效的程序。
請(qǐng)注意,如果你創(chuàng)建太多的線程,程序執(zhí)行的效率實(shí)際上是降低了,而不是提升了。請(qǐng)記住,上下文的切換開銷也很重要,如果你創(chuàng)建了太多的線程,CPU 花費(fèi)在上下文的切換的時(shí)間將多于執(zhí)行程序的時(shí)間!
到此這篇關(guān)于Java實(shí)現(xiàn)模擬機(jī)器人對(duì)話的示例代碼的文章就介紹到這了,更多相關(guān)Java 機(jī)器人對(duì)話內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決Request.getParameter獲取不到特殊字符bug問題
這篇文章主要介紹了解決Request.getParameter獲取不到特殊字符bug問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07Java線程優(yōu)先級(jí)和守護(hù)線程原理解析
這篇文章主要介紹了Java線程優(yōu)先級(jí)和守護(hù)線程原理解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03java兩個(gè)integer數(shù)據(jù)判斷相等用==還是equals
本文主要介紹了java兩個(gè)integer數(shù)據(jù)判斷相等用==還是equals,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12詳解Eclipse 字體、字號(hào)的設(shè)置、最佳字體推薦
這篇文章主要介紹了Eclipse 字體、字號(hào)的設(shè)置、最佳字體推薦,需要的朋友可以參考下2020-09-09Java實(shí)現(xiàn)評(píng)論回復(fù)功能的完整步驟
這篇文章主要給大家介紹了關(guān)于Java實(shí)現(xiàn)評(píng)論回復(fù)功能的完整步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11學(xué)習(xí)Java之如何對(duì)時(shí)間進(jìn)行格式化
當(dāng)我們?cè)谀J(rèn)情況下構(gòu)造出來的時(shí)間對(duì)象,它的時(shí)間格式并不適合我們閱讀,并且在開發(fā)時(shí),pc端、Android端、iOS端等展示的時(shí)間格式可能也并不完全一樣,本文就從這幾個(gè)問題給大家介紹如何對(duì)時(shí)間進(jìn)行格式化,感興趣的同學(xué)可以借鑒一下2023-05-05