日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java笔记 - 黑马程序员_07(多线程,线程同步,线程池,网络编程入门,UDP通信原理,TCP通信原理,commons-io工具类)

發(fā)布時間:2023/12/18 java 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java笔记 - 黑马程序员_07(多线程,线程同步,线程池,网络编程入门,UDP通信原理,TCP通信原理,commons-io工具类) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1. 實現(xiàn)多線程

1.1 進程

進程:是正在運行的程序

  • 是系統(tǒng)進行資源分配鄭調(diào)用的獨立單位
  • 每一個進程都有它自己的內(nèi)存空間和系統(tǒng)資源

1.2 線程

線程:是進程中的單個順序控制流,是一條執(zhí)行路徑

  • 單線程:一個進程如果只有一條執(zhí)行路徑,則稱為單線程程序
  • 多線程:一個進程如果有多條執(zhí)行路徑,則稱為多線程程序

舉例:

  • 記事本程序
  • 掃雷程序

1.3 多線程的實現(xiàn)方式(方式1)

方式1:繼承Thread類

  • 定義一個類MyThread繼承Thread類
  • 在MyThread類中重寫run()方法
  • 創(chuàng)建MyThread類的對象
  • 啟動線程

兩個小問題:

  • 為什么要重寫run() 方法?

因為run() 是用來封裝被線程執(zhí)行的代碼

  • run() 方法和start() 方法的區(qū)別?

run() :封裝線程執(zhí)行的代碼,直接調(diào)用,相當于普通方法的調(diào)用
start():啟動線程;然后由JVM調(diào)用此線程的run()方法

//1.創(chuàng)建線程類繼承Thread類 package demo_01;public class MyThread extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(i);}} }//2. package demo_01;public class MyThreadDemo {public static void main(String[] args) {//創(chuàng)建線程對象MyThread mt1 = new MyThread();MyThread mt2 = new MyThread();//run() :封裝線程執(zhí)行的代碼,直接調(diào)用,相當于普通方法的調(diào)用/* mt1.run();mt2.run();*///void start()導致此線程開始執(zhí)行; Java虛擬機調(diào)用此線程的run方法。mt1.start();mt2.start();} }

1.4 設(shè)置和獲取線程名稱

Thread類中設(shè)置和獲取線程名稱的方法

  • void setName(String name):將此線程的名稱更改為等于參數(shù)name
  • String getName() :返回此線程的名稱
  • 通過構(gòu)造方法也可以設(shè)置線程名稱

如何獲取main() 方法所在的線程名稱?

  • public static Thread currentThread() 返回對當前正在執(zhí)行的線程對象的引用
//1. package demo_02;public class MyThread extends Thread{public MyThread() {}public MyThread(String name) {super(name);}@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName()+":"+i);}} }//2. package demo_02; /*Thread類中設(shè)置和獲取線程名稱的方法void setName(String name):將此線程的名稱更改為等于參數(shù)nameString getName() :返回此線程的名稱 */public class MyThreadDemo {public static void main(String[] args) {MyThread mt1 = new MyThread();MyThread mt2 = new MyThread();//void setName(String name) 將此線程的名稱更改為等于參數(shù) namemt1.setName("高鐵");mt2.setName("飛機");//Thread(String name)分配一個新的Thread對象 // MyThread mt1 = new MyThread("高鐵"); // MyThread mt2 = new MyThread("飛機");mt1.start();mt2.start();//static Thread currentThread()返回對當前正在執(zhí)行的線程對象的引用System.out.println(Thread.currentThread().getName()); //main} }

1.5 線程調(diào)度

線程有兩種調(diào)度模型

  • 分時調(diào)度模型:所有線程輪流使用CPU的使用權(quán),平均分配每個線程占用CPU的時間片
  • 搶占式調(diào)度模型:優(yōu)先讓優(yōu)先級高的線程使用CPU,如果線程的優(yōu)先級相同,那么會隨機選擇一個,優(yōu)先級高的線程獲取的CPU時間片相對多一些
  • Java使用的是搶占式調(diào)度模型

假如計算機只有一個CPU,那么CPU在某一個時刻只能執(zhí)行一條指令,線程只有得到CPU時間片,也就是使用權(quán),
才可以執(zhí)行指令。所以說多線程程序的執(zhí)行是有隨機性,因為誰搶到CPU的使用權(quán)是不一定的

Thread類中設(shè)置和獲取線程優(yōu)先級的方法

  • public final int getPriority() :返回此線程的優(yōu)先級
  • public final void setPriority(int newPriority):更改此線程的優(yōu)先級
  • 線程默認優(yōu)先級是5;線程優(yōu)先級的范圍是:1-10
  • 線程優(yōu)先級高僅僅表示線程獲取的CPU時間片的幾率高,但是要在次數(shù)比較多,或者多次運行的時候才能看到你想要的效果
  • //1. package demo_03;public class ThreadPriority extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName()+":"+i);}} }//2. package demo_03;public class ThreadPriorityDemo {public static void main(String[] args) {//創(chuàng)建線程對象ThreadPriority tp1 = new ThreadPriority();ThreadPriority tp2 = new ThreadPriority();ThreadPriority tp3 = new ThreadPriority();tp1.setName("高鐵");tp2.setName("飛機");tp3.setName("汽車");//public final int getPriority()返回此線程的優(yōu)先級/*System.out.println(tp1.getPriority()); //5System.out.println(tp2.getPriority()); //5System.out.println(tp3.getPriority()); //5*///void setPriority(int newPriority)更改此線程的優(yōu)先級/*System.out.println(Thread.MAX_PRIORITY); //10System.out.println(Thread.MIN_PRIORITY); //1System.out.println(Thread.NORM_PRIORITY); //5*///設(shè)置正確的優(yōu)先級tp1.setPriority(5);tp2.setPriority(10);tp3.setPriority(1);//啟動線程tp1.start();tp2.start();tp3.start();} }

    1.6 線程控制

    方法名說明
    static void sleep(long millis)使當前正在執(zhí)行的線程停留(暫停執(zhí)行)指定的毫秒數(shù)
    void join()等待這個線程死亡
    void setDaemon(boolean on)將此線程標記為守護線程,當運行的線程都是守護線程時,Java虛擬機將退出

    static void sleep(long millis) 使當前正在執(zhí)行的線程停留(暫停執(zhí)行)指定的毫秒數(shù)

    //1. package demo_04;public class ThreadSleep extends Thread {public ThreadSleep() {}public ThreadSleep(String name) {super(name);}@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName() + ":" + i);try {//static void sleep(long millis)使當前正在執(zhí)行的線程以指定的毫秒數(shù)暫停(暫時停止執(zhí)行),具體取決于系統(tǒng)定時器和調(diào)度程序的精度和準確性Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}} }//2. package demo_04;//static void sleep(long millis)使當前正在執(zhí)行的線程以指定的毫秒數(shù)暫停(暫時停止執(zhí)行),具體取決于系統(tǒng)定時器和調(diào)度程序的精度和準確性public class ThreadSleepDemo {public static void main(String[] args) {ThreadSleep ts1 = new ThreadSleep("曹操");ThreadSleep ts2 = new ThreadSleep("劉備");ThreadSleep ts3 = new ThreadSleep("孫權(quán)");ts1.start();ts2.start();ts3.start();} }

    void join() 等待這個線程死亡

    //1. package demo_04;public class ThreadJoin extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName()+":"+i);}} }//2. package demo_04; //void join() 等待這個線程死亡 public class ThreadJoinDemo {public static void main(String[] args) {//創(chuàng)建線程類對象ThreadJoin tj1 = new ThreadJoin();ThreadJoin tj2 = new ThreadJoin();ThreadJoin tj3 = new ThreadJoin();tj1.setName("張三");tj2.setName("李四");tj3.setName("王五");tj1.start();try {//void join() 等待這個線程死亡tj1.join();} catch (InterruptedException e) {e.printStackTrace();}tj2.start();tj3.start();} }

    void setDaemon(boolean on) 將此線程標記為守護線程,當運行的線程都是守護線程時,Java虛擬機將退出

    //1. package demo_04;public class ThreadDaemon extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println();}} }//2. package demo_04; //void setDaemon(boolean on) 將此線程標記為守護線程,當運行的線程都是守護線程時,Java虛擬機將退出 public class ThreadDaemonDemo {public static void main(String[] args) {ThreadDaemon td1 = new ThreadDaemon();ThreadDaemon td2 = new ThreadDaemon();td1.setName("關(guān)羽");td1.setName("張飛");//設(shè)置主線程為劉備Thread.currentThread().setName("劉備");//設(shè)置守護線程td1.setDaemon(true);td2.setDaemon(true);for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+":"+i);}} }

    1.7 線程生命周期

    1.8 多線程的實現(xiàn)方式(方式2)

    方式2:實現(xiàn)Runnable接口

    • 定義一個類MyRunnable實現(xiàn)Runnablef接口
    • 在MyRunnable類中重寫run()方法
    • 創(chuàng)建MyRunnable類的對象
    • 創(chuàng)建Thread類的對象,把MyRunnable對像作為構(gòu)造方法的參數(shù)
    • 啟動線程

    多線程的實現(xiàn)方案有兩種

    • 繼承Thread類
    • 實現(xiàn)Runnable接口

    相比繼承Thread類,實現(xiàn)Runnablef接口的好處

    • 避免了Java單繼承的局限性
    • 適合多個相同程序的代碼去處理同一個資源的情況,把線程和程序的代碼、數(shù)據(jù)有效分離,較好的體現(xiàn)了面向?qū)ο蟮脑O(shè)計思想
    //1. package demo_05;public class MyRunnable implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+":"+i);}} }//2. package demo_05; /* 方式2:實現(xiàn)Runnable接口1.定義一個類MyRunnable:實現(xiàn)Runnablef接口2.在MyRunnable類中重寫run()方法3.創(chuàng)建MyRunnable類的對象4.創(chuàng)建Thread類的對象,把MyRunnable對像作為構(gòu)造方法的參數(shù)5.啟動線程 */ public class MyRunnableDemo {public static void main(String[] args) {//創(chuàng)建MyRunnable類的對象MyRunnable mr = new MyRunnable();//創(chuàng)建Thread類的對象,把MyRunnable對像作為構(gòu)造方法的參數(shù)//public Thread(Runnable target) // Thread t1 = new Thread(mr); // Thread t2 = new Thread(mr);//public Thread(Runnable target, String name)Thread t1 = new Thread(mr,"高鐵");Thread t2 = new Thread(mr,"飛機");//啟動線程t1.start();t2.start();} }

    2. 線程同步

    案例:賣票

    需求:某電影院目前正在上映國產(chǎn)大片,共有100張票,而它有3個窗口賣票,請設(shè)計一個程序模擬該電影院賣票

    思路:
    ①定義一個類SellTicket實現(xiàn)Runnablef接口,里面定義一個成員變量:private int tickets=100;
    ②在SellTicket類中重寫run() 方法實現(xiàn)賣票,代碼步驟如下:
    A:判斷票數(shù)大于0,就賣票,并告知是哪個窗口賣的
    B:賣了票之后,總票數(shù)要減1
    C:票沒有了,也可能有人來問,所以這里用死循環(huán)讓賣票的動作一直執(zhí)行
    ③定義一個測試類SellTicketDemo,里面有main方法,代碼步驟如下
    A:創(chuàng)建SellTicket類的對象
    B:創(chuàng)建三個Thread類的對象,把SellTicket對象作為構(gòu)造方法的參數(shù),并給出對應(yīng)的窗口名稱
    C:啟動線程

    //1.定義一個類SellTicket實現(xiàn)Runnable接口 package demo_06;public class SellTicket implements Runnable {private int tickets = 100;@Overridepublic void run() {while (true) {if (tickets > 0) {System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "張票");tickets--;}}} }//2.定義一個測試類SellTicketDemo,里面有main方法 package demo_06; /* 定義一個測試類SellTicketDemo,里面有main方法,代碼步驟如下A:創(chuàng)建SellTicket類的對象B:創(chuàng)建三個Thread類的對象,把SellTicket對象作為構(gòu)造方法的參數(shù),并給出對應(yīng)的窗口名稱C:啟動線程 */public class SellTicketDemo {public static void main(String[] args) {//創(chuàng)建SellTicket類的對象SellTicket st = new SellTicket();//創(chuàng)建三個Thread類的對象,把SellTicket對象作為構(gòu)造方法的參數(shù),并給出對應(yīng)的窗口名稱Thread th1 = new Thread(st,"窗口1");Thread th2 = new Thread(st,"窗口2");Thread th3 = new Thread(st,"窗口3");//啟動線程th1.start();th2.start();th3.start();} }

    2.1 買票案例的思考

    剛才講解了電影院賣票程序,好像沒有什么問題。但是在實際生活中,售票時出票也是需要時間的所以,在出售一張票的時候,需要一點時間的延遲,接下來我們?nèi)バ薷馁u票程序中賣票的動作:每次出票時間100毫秒,用sleep() 方法實現(xiàn)

    賣票出現(xiàn)了問題:

  • 相同的票出現(xiàn)了多次
  • 出現(xiàn)了負數(shù)的票
  • 問題原因:

    • 線程執(zhí)行的隨機性導致的
    //1. package demo_06;public class SellTicket implements Runnable {private int tickets = 100;@Overridepublic void run() {//相同的票出現(xiàn)了多次while (true) {/* if (tickets > 0) {//通過sleep() 方法來模擬出票時間try {Thread.sleep(1000);//t1線程休息1000毫秒//t2線程搶到cpu執(zhí)行權(quán),t2線程開始執(zhí)行,執(zhí)行到這里,t2休息1000毫秒//t3線程搶到cpu執(zhí)行權(quán),t3線程開始執(zhí)行,執(zhí)行到這里,t3休息1000毫秒} catch (InterruptedException e) {e.printStackTrace();}//按照線程順序醒來//t1搶到線程執(zhí)執(zhí)行權(quán),在控制臺輸出:窗口1正在出售第100張票System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "張票");//t2搶到線程執(zhí)執(zhí)行權(quán),在控制臺輸出:窗口2正在出售第100張票//t3搶到線程執(zhí)執(zhí)行權(quán),在控制臺輸出:窗口3正在出售第100張票tickets--;//如果這三個線程按照順序,執(zhí)行到這里執(zhí)行了3次--操作,最終票變成了97}*///出現(xiàn)負數(shù)票的情況if (tickets > 0) {//通過sleep() 方法來模擬出票時間try {Thread.sleep(10);//t1線程休息1000毫秒//t2線程搶到cpu執(zhí)行權(quán),t2線程開始執(zhí)行,執(zhí)行到這里,t2休息1000毫秒//t3線程搶到cpu執(zhí)行權(quán),t3線程開始執(zhí)行,執(zhí)行到這里,t3休息1000毫秒} catch (InterruptedException e) {e.printStackTrace();}//假設(shè)線程按照順序醒過來//t1搶到了CPU的執(zhí)行權(quán),在控制臺輸出:窗口1正在出售第1張票//假設(shè)t1繼續(xù)擁有cPU的執(zhí)行權(quán),就會執(zhí)行tickets--;操作,tickets=0;//t2搶到了CPU的執(zhí)行權(quán),在控制臺輸出:窗口1正在出售第0張票//假設(shè)t2繼續(xù)擁有CPU的執(zhí)行權(quán),就會執(zhí)行tickets--;操作,tickets=-1;//t3搶到了CPU的執(zhí)行權(quán),在控制臺輸出:窗口3正在出售第-1張票//假設(shè)t2繼續(xù)擁有CPU的執(zhí)行權(quán),就會執(zhí)行tickets-;操作,tickets=-2;System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "張票");tickets--;}}} }//2. package demo_06; /* 定義一個測試類SellTicketDemo,里面有main方法,代碼步驟如下A:創(chuàng)建SellTicket類的對象B:創(chuàng)建三個Thread類的對象,把SellTicket對象作為構(gòu)造方法的參數(shù),并給出對應(yīng)的窗口名稱C:啟動線程 */public class SellTicketDemo {public static void main(String[] args) {//創(chuàng)建SellTicket類的對象SellTicket st = new SellTicket();//創(chuàng)建三個Thread類的對象,把SellTicket對象作為構(gòu)造方法的參數(shù),并給出對應(yīng)的窗口名稱Thread th1 = new Thread(st,"窗口1");Thread th2 = new Thread(st,"窗口2");Thread th3 = new Thread(st,"窗口3");//啟動線程th1.start();th2.start();th3.start();} }

    2.2 賣票案例數(shù)據(jù)安全問題解決

    為什么出現(xiàn)問題?(這也是我們判斷多線程程序是否會有數(shù)據(jù)安全問題的標準)

    • 是否是多線程環(huán)境
    • 是否有共享數(shù)據(jù)
    • 是否有多條語句操作共享數(shù)據(jù)

    如何解決多線程安全問題呢?

    • 基本思想:讓程序沒有安全問題的環(huán)境

    怎么實現(xiàn)呢?

    • 把多條語句操作共享數(shù)據(jù)的代碼給鎖起來,讓任意時刻只能有一個線程執(zhí)行即可
    • Java提供了同步代碼塊的方式來解決(如下2.3)

    2.3 同步代碼塊

    鎖多條語句操作共享數(shù)據(jù),可以使用同步代碼塊實現(xiàn)

    格式:

    synchronized(任意對象){多條語句操作共享數(shù)據(jù)的代碼 }
    • synchronized(任意對象):就相當于給代碼加鎖了,任意對象就可以看成是一把鎖

    同步的好處和弊端:

    • 好處:解決了多線程的數(shù)據(jù)安全問題
    • 弊端:當線程很多時,因為每個線程都會去判斷同步上的鎖,這是很耗費資源的,無形中會降低程序的運行效率
    //1. package demo_06;public class SellTicket implements Runnable {private int tickets = 100;private Object obj = new Object();@Overridepublic void run() {while (true) {synchronized (obj){//t1進來后就會把這段代碼鎖起來if (tickets > 0) {try {Thread.sleep(100);//t1線程休息100毫秒} catch (InterruptedException e) {e.printStackTrace();}//窗口正在出售第100張票System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "張票");tickets--;}}//t1出來了,這段代碼就被釋放了}} }//2. package demo_06;public class SellTicketDemo {public static void main(String[] args) {//創(chuàng)建SellTicket類的對象SellTicket st = new SellTicket();//創(chuàng)建三個Thread類的對象,把SellTicket對象作為構(gòu)造方法的參數(shù),并給出對應(yīng)的窗口名稱Thread th1 = new Thread(st,"窗口1");Thread th2 = new Thread(st,"窗口2");Thread th3 = new Thread(st,"窗口3");//啟動線程th1.start();th2.start();th3.start();} }

    2.4 同步方法

    同步方法:就是把synchronized關(guān)鍵字加到方法上

    格式:

    修飾符 synchronized 返回值類型 方法名(方法參數(shù)){}

    同步方法的鎖對象是什么呢?

    • this

    同步靜態(tài)方法:就是把synchronized關(guān)鍵字加到靜態(tài)方法上

    格式:

    修飾符 static synchronized 返回值類型 方法名(方法參數(shù)){}

    同步靜態(tài)方法的鎖對象是什么呢?

    • 類名.class
    //1. package demo_06;public class SellTicket implements Runnable {private static int tickets = 100;private Object obj = new Object();private int x = 0;@Overridepublic void run() {while (true) {if (x % 2 == 0) { // synchronized (obj) { // synchronized (this) { //同步方法鎖synchronized (SellTicket.class) { //靜態(tài)同步方法鎖//t1進來后就會把這段代碼鎖起來if (tickets > 0) {try {Thread.sleep(10);//t1線程休息100毫秒} catch (InterruptedException e) {e.printStackTrace();}//窗口正在出售第100張票System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "張票");tickets--;}}} else {sellticks();}x++;}}/*private void sellticks() {synchronized (obj) {//t1進來后就會把這段代碼鎖起來if (tickets > 0) {try {Thread.sleep(100);//t1線程休息100毫秒} catch (InterruptedException e) {e.printStackTrace();}//窗口正在出售第100張票System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "張票");tickets--;}}}*///同步方法/*private synchronized void sellticks() {if (tickets > 0) {try {Thread.sleep(100);//t1線程休息100毫秒} catch (InterruptedException e) {e.printStackTrace();}//窗口正在出售第100張票System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "張票");tickets--;}}*///靜態(tài)同步方法private static synchronized void sellticks() {if (tickets > 0) {try {Thread.sleep(100);//t1線程休息100毫秒} catch (InterruptedException e) {e.printStackTrace();}//窗口正在出售第100張票System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "張票");tickets--;}} }//2. package demo_06;public class SellTicketDemo {public static void main(String[] args) {//創(chuàng)建SellTicket類的對象SellTicket st = new SellTicket();//創(chuàng)建三個Thread類的對象,把SellTicket對象作為構(gòu)造方法的參數(shù),并給出對應(yīng)的窗口名稱Thread th1 = new Thread(st,"窗口1");Thread th2 = new Thread(st,"窗口2");Thread th3 = new Thread(st,"窗口3");//啟動線程th1.start();th2.start();th3.start();} }

    2.5 線程安全的類

    StringBuffer

    • 線程安全,可變的字符序列
    • 從版本JDK5開始,被StringBuilder替代。通常應(yīng)該使用StringBuilder類,因為它支持所有相同的操作,但它更快,因為它不執(zhí)行同步

    Vector

    • 從Java2平臺v1.2開始,該類改進了List接口,使其成為Java Collections Framework的成員。與新的集合實現(xiàn)不同,Vector被同步。如果不需要線程安全的實現(xiàn),建議使用ArrayList代替Vector

    Hashtable

    • 該類實現(xiàn)了一個哈希表,它將鍵映射到值。任何非null對像都可以用作鍵或者值
    • 從Java2平臺v1.2開始,該類進行了改進,實現(xiàn)了Map接口,使其成為Java Collections Framework的成員。
      與新的集合實現(xiàn)不同,Hashtable被同步。如果不需要線程安全的實現(xiàn),建議使用HashMap代替Hashtable
    package demo_07;import java.util.*;/* 線程的安全類:StringBufferVertorHashtable*/ public class ThreadDemo {public static void main(String[] args) {StringBuffer sb2 = new StringBuffer();StringBuilder sb = new StringBuilder();Vector<String> v = new Vector<String>();ArrayList<String> array = new ArrayList<String>();Hashtable<String, String> ht = new Hashtable<String, String>();HashMap<String, String> hm = new HashMap<String, String>();//public static <T> List<T> synchronizedList(List<T> list)返回由指定列表支持的同步(線程安全)列表List<String> lise = Collections.synchronizedList(new ArrayList<String>());} }

    2.6 Lock鎖

    雖然我們可以理解同步代碼塊和同步方法的鎖對象問題,但是我們并沒有直接看到在哪里加上了鎖,在哪里釋放了鎖,為了更清晰的表達如何加鎖和釋放鎖,JDK5以后提供了一個新的鎖對象Lock

    Lock實現(xiàn)提供比使用synchronized() 方法和語句可以獲得更廣泛的鎖定操作
    Lock中提供了獲得鎖和釋放鎖的方法:

    • void lock() :獲得鎖
    • void unlock() :釋放鎖

    Lock是接口不能直接實例化,這里采用它的實現(xiàn)類ReentrantLock來實例化
    ReentrantLock的構(gòu)造方法:

    • ReentrantLock() :創(chuàng)建一個ReentrantLock的實例
    //1. package demo_08;import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class SellTicket implements Runnable {private int tickets = 100;private Lock lock = new ReentrantLock();@Overridepublic void run() {while (true) {try {lock.lock();if (tickets > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "張票");tickets--;}} finally {lock.unlock();}}} }//2. package demo_08;public class SellTicketDemo {public static void main(String[] args) {SellTicket st = new SellTicket();Thread th1 = new Thread(st,"窗口1");Thread th2 = new Thread(st,"窗口2");Thread th3 = new Thread(st,"窗口3");th1.start();th2.start();th3.start();} }

    3 線程池

    Java提供了線程池技術(shù),讓線程可以重復利用,解決線程頻繁創(chuàng)建和銷毀的問題,提高運行效率。

    創(chuàng)建線程池

    Executors類是線程池的工具類,通過Executors.工具類可以創(chuàng)建線程池。

    方法說明
    static ExecutorService newFixedThreadPool(int nThreads)創(chuàng)建一個線程池,參數(shù)為池中的線程數(shù)。

    使用線程池

    • ExecutorService代表線程池,該類中提供了submit方法用于處理提交的任務(wù)。
    • 調(diào)用submit(任務(wù))方法時,線程池會分配池中空閑的線程去執(zhí)行對應(yīng)的任務(wù)。
    方法名說明
    submit(Runnable task)提交Runnable類型的任務(wù)
    submit(Callabletask)提交Callable類型的任務(wù)
    void shutdown()關(guān)閉連接池

    創(chuàng)建任務(wù)的兩種方式:

    • 實現(xiàn)Runnable:接口,重寫run方法。
    • 實現(xiàn)Callable<返回值類型>接口,重寫call方法。

    3.1 線程池Runable方法(案例)

    //1. package com.demo_2線程池Runnable接口;public class MyRunnable implements Runnable{@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println(name+"執(zhí)行了...");} }//2.測試類 package com.demo_2線程池Runnable接口;import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class Demo {public static void main(String[] args) {//創(chuàng)建線程數(shù)量為3的線程池ExecutorService pool = Executors.newFixedThreadPool(3);//使用線程池提交任務(wù)// submit(任務(wù))MyRunnable mr = new MyRunnable();pool.submit(mr);pool.submit(mr);pool.submit(mr);pool.submit(mr);//線程池中的線程都是出于活躍狀態(tài),代碼不會停止//把線程池關(guān)閉,線程會全部銷毀//pool.shutdown(); //不要關(guān)閉} }

    3.2 線程池Callable方法(案例)

    //1. package com.demo_3Callable方法;import java.util.Random; import java.util.concurrent.Callable;public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {String name = Thread.currentThread().getName();System.out.println(name+"執(zhí)行了....");Random r = new Random();int i = r.nextInt(10);System.out.println(i);return i;} }//2.測試類 package com.demo_3Callable方法;import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future;public class CallableDemo {public static void main(String[] args) throws Exception {//創(chuàng)建線程池ExecutorService pool = Executors.newFixedThreadPool(3);//提交MyCallable任務(wù)MyCallable mc = new MyCallable();//線程池執(zhí)行任務(wù),并把返回值裝到future中Future<Integer> f = pool.submit(mc);//通過future的get方法,獲取返回值Integer a = f.get();System.out.println("a="+a);//關(guān)閉線程池(不建議使用,線程池關(guān)閉線程里的東西全部銷毀)pool.shutdown();} }

    案例:使用線程池完成求和計算

    需求:使用線程池方式創(chuàng)建兩個線程任務(wù):分段計算1~20000之間的數(shù)字和。
    1.線程1計算1~10000之間的數(shù)字和,并返回結(jié)果。
    2.線程2計算10001~20000之間的數(shù)字和,并返回結(jié)果。
    3.提交任務(wù),獲取計算結(jié)果進行合并,打印最終結(jié)果。
    分析:因為要返回求和結(jié)果,使用Callable來實現(xiàn)。

    package com.demo_3Callable方法;import java.util.concurrent.*;public class Demo {public static void main(String[] args) throws Exception {//創(chuàng)建線程池ExecutorService pool = Executors.newFixedThreadPool(3);//線程池任務(wù)(計算1-10000)Future<Integer> f1 = pool.submit(new Callable<Integer>() {@Overridepublic Integer call() throws Exception {String name = Thread.currentThread().getName();System.out.println(name + "線程執(zhí)行了......");int sum = 0;for (int i = 1; i <= 10000; i++) {sum += i;}return sum;}});//線程池任務(wù)(計算10001-20000)Future<Integer> f2 = pool.submit(new Callable<Integer>() {@Overridepublic Integer call() throws Exception {String name = Thread.currentThread().getName();System.out.println(name + "線程執(zhí)行了......");int sum = 0;for (int i = 1; i <= 10000; i++) {sum += i;}return sum;}});//獲取返回值Integer result1 = f1.get();Integer result2 = f2.get();System.out.println("最終的結(jié)果為" + result1 + result2);} }

    4. 網(wǎng)絡(luò)編程入門

    4.1 網(wǎng)絡(luò)編程概述

    計算機網(wǎng)絡(luò)

    • 是指將地理位置不同的具有獨立功能的多臺計算機及其外部設(shè)備,通過通信線路連接起來,在網(wǎng)絡(luò)操作系統(tǒng),網(wǎng)絡(luò)管理軟件及網(wǎng)絡(luò)通信協(xié)議的管理和協(xié)調(diào)下,實現(xiàn)資源共享和信息傳遞的計算機系統(tǒng)

    網(wǎng)絡(luò)編程

    • 在網(wǎng)絡(luò)通信協(xié)議下,實現(xiàn)網(wǎng)絡(luò)互連的不同計算機上運行的程序間可以進行數(shù)據(jù)交換

    4.2 網(wǎng)絡(luò)編程三要素

    IP地址

    • 要想讓網(wǎng)絡(luò)中的計算機能夠互相通信,必須為每臺計算機指定一個標識號,通過這個標識號來指定要接收數(shù)據(jù)的計算機和識別發(fā)送的計算機,而P地址就是這個標識號。也就是設(shè)備的標識

    端口

    • 網(wǎng)絡(luò)的通信,本質(zhì)上是兩個應(yīng)用程序的通信。每臺計算機都有很多的應(yīng)用程序,那么在網(wǎng)絡(luò)通信時,如何區(qū)分這些應(yīng)用程序呢?如果說P地址可以唯一標識網(wǎng)絡(luò)中的設(shè)備,那么端口號就可以唯一標識設(shè)備中的應(yīng)用程序了。也就是應(yīng)用程序的標識

    協(xié)議

    • 通過計算機網(wǎng)絡(luò)可以使多臺計算機實現(xiàn)連接,位于同一個網(wǎng)絡(luò)中的計算機在進行連接和通信時需要遵守一定的規(guī)則,這就好比在道路中行駛的汽車一定要遵守交通規(guī)唄則一樣。在計算機網(wǎng)絡(luò)中,這些連接和通信的規(guī)則被稱為網(wǎng)絡(luò)通信協(xié)議,它對數(shù)據(jù)的傳輸格式、傳輸速率、傳輸步驟等做了統(tǒng)一規(guī)定,通信雙方必須同時遵守才能完成數(shù)據(jù)交換。常見的協(xié)議有UDP協(xié)議和TCP協(xié)議

    4.3 IP地址

    IP地址:是網(wǎng)絡(luò)中設(shè)備的唯一標識

    P地址分為兩大類

    • IPv4:是給每個連接在網(wǎng)絡(luò)上的莊機分配一個32bi地址。按照TCP八P規(guī)定,P地址用二進制來表示,每個P地址長32bit,也就是4個字節(jié)。例破如一個采用二進制形式的P地址是“11000000101010000000000101000010°”,這么長的地址,處理起來也太費勁了。為了方便使用,P地址經(jīng)常被寫成十進制的形式,中間使用符號“”分隔不同的字節(jié)。于是,上面的1P地址可以表示為"192.168.1.66”。P地址的這種表示法叫做“點分十進制表示法”,這顯然比1和0容易記憶得多
    • IPV6:由于互聯(lián)網(wǎng)的蓬勃發(fā)展,P地址的需求量愈來愈大,但是網(wǎng)絡(luò)地址資源有限,使得P的分配越發(fā)緊張。為了擴大地址空間,通過Pv6重新定義地址空間,采用128位地址長度,每16個字節(jié)一組,分成8組十六進制數(shù),這樣就解決了網(wǎng)絡(luò)地址資源數(shù)量不夠的問題

    常用命令:

    • ipconfig:查看本機IP地址
    • ping IP地址:檢查網(wǎng)絡(luò)是否連通

    特殊IP地址:

    • 127.0.0.1:是回送地址,可以代表本機地址,般用來測試使用

    4.4 InetAddress的使用

    為了方便我們對lP地址的獲取和操作,Java提供了一個類InetAddress供我們使用

    InetAddress:此類表示Internet協(xié)議 (IP) 地址

    方法明說明
    static InetAddress getByName(String host)確定主機名稱的P地址。主機名稱可以是機器名稱,也可以是P地址
    String getHostName()獲取此P地址的主機名
    String getHostAddress()返回文本顯示中的IP地址字符串
    package demo_01; /*InetAddress:此類表示Internet協(xié)議 (IP) 地址static InetAddress getByName(String host)確定主機名稱的P地址。主機名稱可以是機器名稱,也可以是P地址String getHostName()獲取此P地址的主機名String getHostAddress()返回文本顯示中的IP地址字符串 */ import java.io.IOException; import java.net.InetAddress;public class InetAddressDemo {public static void main(String[] args) throws IOException {//static InetAddress getByName(String host)確定主機名稱的IP地址。主機名稱可以是機器名稱,也可以是IP地址InetAddress address = InetAddress.getByName("192.168.31.81");//String getHostName()獲取此P地址的主機名String name = address.getHostName();//String getHostAddress()返回文本顯示中的IP地址字符串String ip = address.getHostAddress();System.out.println("主機名:"+name);System.out.println("IP地址:"+ip);} }

    4.5 端口

    端口:設(shè)備上應(yīng)用程序的唯一標識

    端口號:用兩個字節(jié)表示的整數(shù),它的取值范圍是065535。其中,01023之間的端口號用于一些知名的網(wǎng)絡(luò)服務(wù)和應(yīng)用,普通的應(yīng)用程序需要使用1024以上的端口號。如果端口號被另外一個服務(wù)或應(yīng)用所占用,會導致當前程序啟動失敗

    4.6 協(xié)議

    協(xié)議:計算機網(wǎng)絡(luò)中,連接和通信的規(guī)則被稱為網(wǎng)絡(luò)通信協(xié)議

    UDP協(xié)議:

    • 用戶數(shù)據(jù)報協(xié)議(User Datagram Protocol)
    • UDP是無連接通信協(xié)議,即在數(shù)據(jù)傳輸時,數(shù)據(jù)的發(fā)送端和接收端不建立邏輯連接。簡單來說,當一臺計算機向另外一臺計算機發(fā)送數(shù)據(jù)時,發(fā)送端不會確認接收端是否存在,就會發(fā)出數(shù)據(jù),同樣接收端在收到數(shù)據(jù)時,也不會向發(fā)送端反饋是否收到數(shù)據(jù)。由于使用UDP協(xié)議消耗資源小,通信效率高,所以通常都會用于音頻、視頻和普通數(shù)據(jù)的傳輸
    • 例如視頻會議通常采用UDP協(xié)議,因為這種情況即使偶爾丟失一兩個數(shù)據(jù)包,也不會對接收結(jié)果產(chǎn)生太大影響。但是在使用UDP協(xié)議傳送數(shù)據(jù)時,由于UDP的面向無連接性,不能保證數(shù)據(jù)的完整性,因此在傳輸重要數(shù)據(jù)時不建議使用UDP協(xié)議

    TCP協(xié)議:

    • 傳輸控制協(xié)議(Transmission Control Protocol)
    • TCP協(xié)議是面向連接的通信協(xié)議,即傳輸數(shù)據(jù)之前,在發(fā)送端和接收總建立邏輯連接,然后再傳輸數(shù)據(jù),
      它提供了兩臺計算機之間可靠無差錯的數(shù)據(jù)傳輸。在TCP連接中必須要明確客戶端與服務(wù)器端,由客戶端
      向服務(wù)端發(fā)出連接請求,每次連接的創(chuàng)建都需要經(jīng)過“三次握手”
    • 三次握手:TCP協(xié)議中,在發(fā)送數(shù)據(jù)的準備階段,客戶端與服務(wù)器之間的三次交互,以保證連接的可靠
      第一次握手,客戶端向服務(wù)器端發(fā)出連接請求,等待服務(wù)器確認
      第二次握手,服務(wù)器端向客戶端回送一個響應(yīng),通知客戶端收到了連接請求
      第三次握手,客戶端再次向服務(wù)器端發(fā)送確認信息,確認連接
    • 完成三次握手,連接建立后,客戶端和服務(wù)器就可以開始進行數(shù)據(jù)傳輸了。由于這種面向連接的特性,TCP協(xié)議可以保證傳輸數(shù)據(jù)的安全,所以應(yīng)用十分廣泛。例如上傳文件、下載文件、瀏覽網(wǎng)頁等

    5. UDP通信原理

    5.1 UDP通信原理

    UDP協(xié)議是一種不可靠的網(wǎng)絡(luò)協(xié)議,它在通信的兩端各建立一個Socket對象,但是這兩個Socket只是發(fā)送,接收數(shù)據(jù)的對象因此對于基于UDP協(xié)議的通信雙方而言,沒有所謂的客戶端和服務(wù)器的概念Java提供了DatagramSocket類作為基于UDP協(xié)議的Socket

    5.2 UDP發(fā)送數(shù)據(jù)

    發(fā)送數(shù)據(jù)的步驟

    ①創(chuàng)建發(fā)送端的Socket對象(DatagramSocket)

    DatagramScoket()

    ②創(chuàng)建數(shù)據(jù),并把數(shù)據(jù)打包

    DatagramPacket(byte[]buf,int length,InetAddress address,int port)

    ③調(diào)用DatagramSocket對象的方法發(fā)送數(shù)據(jù)

    void send(DatagramPacket p)

    ④關(guān)閉發(fā)送端

    void close() package demo_02;/* 發(fā)送數(shù)據(jù)的步驟:創(chuàng)建發(fā)送端的Socket對象(DatagramSocket)創(chuàng)建數(shù)據(jù),并把數(shù)據(jù)打包調(diào)用DatagramSocket對象的方法發(fā)送數(shù)據(jù)關(guān)閉發(fā)送端 */import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress;public class SendDemo {public static void main(String[] args) throws IOException {//創(chuàng)建發(fā)送端的Socket對象(DatagramSocket)//DatagramSocket()//構(gòu)造數(shù)據(jù)報套接字并將其綁定到本地主機上的任何可用端口DatagramSocket ds = new DatagramSocket();//創(chuàng)建數(shù)據(jù),并把數(shù)據(jù)打包//DatagramPacket(byte[] buf, int length, InetAddress address, int port)//構(gòu)造用于發(fā)送長度的分組的數(shù)據(jù)報包 length指定主機上到指定的端口號byte[] bys = "hello,java".getBytes();DatagramPacket dp = new DatagramPacket(bys,bys.length, InetAddress.getByName("192.168.31.81"),10010);//調(diào)用DatagramSocket對象的方法發(fā)送數(shù)據(jù)//void send(DatagramPacket p)從此套接字發(fā)送數(shù)據(jù)報包 ds.send(dp);//關(guān)閉發(fā)送端ds.close();} }

    5.3 UDP接收數(shù)據(jù)

    接收數(shù)據(jù)的步驟

    ①創(chuàng)建接收端的Socket對象(DatagramSocket)

    DatagramSocket(int port)

    ②創(chuàng)建一個數(shù)據(jù)包,用于接收數(shù)據(jù)

    DatagramPacket(byte[]buf,int length)

    ③調(diào)用DatagramSocket對象的方法接收數(shù)據(jù)

    void receive(DatagramPacket p)

    ④解析數(shù)據(jù)包,并把數(shù)據(jù)在控制臺顯示

    byte[] getData() int getLength()

    ⑤關(guān)閉接收端

    void close() package demo_02; /* 接收數(shù)據(jù)的步驟:創(chuàng)建接收端的Socket對象(DatagramSocket)創(chuàng)建一個數(shù)據(jù)包,用于接收數(shù)據(jù)調(diào)用DatagramSocket對象的方法接收數(shù)據(jù)解析數(shù)據(jù)包,并把數(shù)據(jù)在控制臺顯示關(guān)閉接收端 */ import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket;public class ReceiveDemo {public static void main(String[] args) throws IOException {//創(chuàng)建接收端的Socket對象(DatagramSocket)DatagramSocket ds = new DatagramSocket(10010);//創(chuàng)建一個數(shù)據(jù)包,用于接收數(shù)據(jù)byte[] bys = new byte[1024];DatagramPacket dp = new DatagramPacket(bys,bys.length);//調(diào)用DatagramSocket對象的方法接收數(shù)據(jù)ds.receive(dp);//解析數(shù)據(jù)包,并把數(shù)據(jù)在控制臺顯示byte[] datas = dp.getData();int len = dp.getLength();System.out.println("數(shù)據(jù)是:"+new String(datas,0,len));//關(guān)閉接收端ds.close();} }

    5.4 UDP通信程序練習

    按照下面的要求實現(xiàn)程序:

  • UDP發(fā)送數(shù)據(jù):數(shù)據(jù)來自于鍵盤錄入,直到輸入的數(shù)據(jù)是886,發(fā)送數(shù)據(jù)結(jié)束
  • UDP接收數(shù)據(jù):因為接收端不知道發(fā)送端什么時候停止發(fā)送,故采用死循環(huán)接收
  • //1.發(fā)送端 package demo_03;import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress;public class SendDemo {public static void main(String[] args) throws IOException {//創(chuàng)建發(fā)送端的Scoket對象(DatagramScoket());DatagramSocket ds = new DatagramSocket();//自己封裝鍵盤錄入數(shù)據(jù)BufferedReader br = new BufferedReader(new InputStreamReader(System.in));String line;while ((line=br.readLine())!=null){//輸入的數(shù)據(jù)為886,發(fā)送數(shù)據(jù)結(jié)束if ("886".equals(line)){break;}//創(chuàng)建數(shù)據(jù)并把數(shù)據(jù)打包byte[] bys = line.getBytes();DatagramPacket dp = new DatagramPacket(bys,bys.length, InetAddress.getByName("192.168.31.81"),10010);//調(diào)用DagramScoket對象的方法發(fā)送數(shù)據(jù)ds.send(dp);//關(guān)閉發(fā)送端 // ds.close();}} }//2.服務(wù)器端 package demo_03;import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException;public class ServerDemo {public static void main(String[] args) throws IOException {//創(chuàng)建結(jié)束端的Scoket對象(DatagramScoket)DatagramSocket ds = new DatagramSocket(10010);while (true){//創(chuàng)建數(shù)據(jù)包用于接收數(shù)據(jù)byte[] bys = new byte[1024];DatagramPacket dp = new DatagramPacket(bys,bys.length);//調(diào)用DatagramScoket對象的方法接收數(shù)據(jù)ds.receive(dp);//解析數(shù)據(jù)包,并把數(shù)據(jù)輸出在控制臺System.out.println("數(shù)據(jù)是:"+new String(dp.getData(),0,dp.getLength()));}//關(guān)閉接收端 // ds.close();} }

    6. TCP通信原理

    6.1 TCP通信原理

  • TCP通信協(xié)議是一種可靠的網(wǎng)絡(luò)協(xié)議,它在通信的兩端各建立一個Socket對象,從而在通信的兩端新形成網(wǎng)絡(luò)虛擬鏈路,一旦建立了虛擬的網(wǎng)絡(luò)鏈路,兩端的程序就河以通過虛擬鏈路進行通信
  • Java對基于TCP協(xié)議的的網(wǎng)絡(luò)提供了良好的封裝,使用Socket對象來代表兩端的通信端口,并通過Socket產(chǎn)生IO流來進行網(wǎng)絡(luò)通信
  • Java為客戶端提供了Socket類,為服務(wù)器端提供了ServerSocket類
  • 6.2 TCP發(fā)送數(shù)據(jù)

    發(fā)送數(shù)據(jù)的步驟:

    ①創(chuàng)建客戶端的Socket對象(Socket)

    Socket(String host,int port)

    ②獲取輸出流,寫數(shù)據(jù)

    OutputStream getOutputStream()

    ③釋放資源

    void close() package TCPDemo_04; /* 發(fā)送數(shù)據(jù)的步驟創(chuàng)建客戶端的Socket對象(Socket)獲取輸出流,寫數(shù)據(jù)科放資源 */ import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket;public class ClientDemo {public static void main(String[] args) throws IOException {//創(chuàng)建客戶端的Socket對象(Socket)//Socket(InetAddress address, int port)創(chuàng)建流套接字并將其連接到指定IP地址的指定端口號 // Socket s = new Socket(InetAddress.getByName("192.168.31.81"),10010);//Socket(String host, int port)創(chuàng)建流套接字并將其連接到指定主機上的指定端口號Socket s= new Socket("192.168.31.81",10010);//獲取輸出流,寫數(shù)據(jù)//OutputStream getOutputStream()返回此套接字的輸出流OutputStream os = s.getOutputStream();os.write("hello,java".getBytes());//釋放資源s.close();} }

    6.3 TCP接受數(shù)據(jù)

    接收數(shù)據(jù)的步驟:

    ①創(chuàng)建服務(wù)器端的Socket對象(ServerSocket)

    ServerSocket(int port)

    ②監(jiān)聽客戶端連接,返回一個Socket對象

    Socket accept()

    ③獲取輸入流,讀數(shù)據(jù),并把數(shù)據(jù)顯示在控制臺

    InputStream getlnputStream()

    ④釋放資源

    void close() package TCPDemo_04; /* 接收數(shù)據(jù)的步驟:創(chuàng)建服務(wù)器端的Socket對象(ServerSocket)監(jiān)聽客戶端連接,返回一個Sockety對象獲取輸入流,讀數(shù)據(jù),并把數(shù)據(jù)顯示在控制臺釋放資源 */ import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket;public class Serverdemo {public static void main(String[] args) throws IOException {//創(chuàng)建服務(wù)器端的Socket對象(ServerScoket)//ServerSocket(int port)創(chuàng)建綁定到指定端口的服務(wù)器套接字ServerSocket ss = new ServerSocket(10010);//監(jiān)聽客戶端連接,返回一個Sockety對象Socket s = ss.accept();//獲取輸入流,讀數(shù)據(jù),并把數(shù)據(jù)顯示在控制臺InputStream is = s.getInputStream();byte[] bys = new byte[1024];int len = is.read(bys);String data = new String(bys,0,len);System.out.println("數(shù)據(jù)是:"+data);//釋放資源ss.close();s.close();} }

    6.4 TCP通信程序練習

    練習1

    • 客戶端:發(fā)送數(shù)據(jù),接收服務(wù)器反饋
    • 服務(wù)端:接收數(shù)據(jù),給出反饋
    //服務(wù)器端 package TcpPracticeDemo_01;import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; //服務(wù)端:接收數(shù)據(jù),給出反饋 public class ServerDemo {public static void main(String[] args) throws IOException {//創(chuàng)建服務(wù)器Socket對象(ServerSocket)ServerSocket ss = new ServerSocket(10010);//監(jiān)聽客戶端連接,返回Socket對象Socket s = ss.accept();//獲取輸入流,讀數(shù)據(jù),并把數(shù)據(jù)顯示在控制臺InputStream is = s.getInputStream();byte[] bys = new byte[1024];int len = is.read(bys);String data = new String(bys,0,len);System.out.println("服務(wù)器"+data);/*int len;while ((len = is.read(bys)) != -1) {String data = new String(bys,0,len);System.out.println("服務(wù)器"+data);}*///給出反饋OutputStream os = s.getOutputStream();os.write("over".getBytes());//釋放資源ss.close();} }//客戶端 package TcpPracticeDemo_01;import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; //客戶端:發(fā)送數(shù)據(jù),接收服務(wù)器反饋 public class ClientDemo {public static void main(String[] args) throws IOException {//創(chuàng)建客戶端Socket對象Socket ss = new Socket("192.168.31.81",10010);//獲取輸出流,寫數(shù)據(jù)OutputStream os = ss.getOutputStream();os.write("hello,java".getBytes());//接受服務(wù)器反饋InputStream is = ss.getInputStream();byte[] bys = new byte[1024];int len;while ((len = is.read(bys)) != -1) {System.out.println("客戶端:" + new String(bys, 0, len));}//釋放資源ss.close();} }

    練習2

    • 客戶端:數(shù)據(jù)來自于鍵盤錄入,直到輸入的數(shù)據(jù)是886,發(fā)送數(shù)據(jù)結(jié)束
    • 服務(wù)器:接收到的數(shù)據(jù)在控制臺輸出
    //服務(wù)器端 package TPracticeDemo_02;import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket;//服務(wù)器:接收到的數(shù)據(jù)在控制臺輸出 public class ServerDemo {public static void main(String[] args) throws IOException {//創(chuàng)建服務(wù)器端Socket對象ServerSocket ss = new ServerSocket(10010);//監(jiān)聽客戶端連接,返回對應(yīng)的Socket對象Socket s = ss.accept();//獲取輸入流BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));String line;while ((line = br.readLine()) != null) {String data = new String(line);System.out.println("服務(wù)器:"+data);}//釋放資源ss.close();} }//客戶端 package TPracticeDemo_02;import java.io.*; import java.net.InetAddress; import java.net.Socket;//客戶端:數(shù)據(jù)來自于鍵盤錄入,直到輸入的數(shù)據(jù)是886,發(fā)送數(shù)據(jù)結(jié)束 public class ClientDemo {public static void main(String[] args) throws IOException {//創(chuàng)建客戶端Socket對象Socket s= new Socket(InetAddress.getByName("192.168.31.81",10010);//數(shù)據(jù)來自鍵盤錄入,輸入886,發(fā)送結(jié)束BufferedReader be = new BufferedReader(new InputStreamReader(System.in));//封裝輸出數(shù)據(jù)流BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));String line;while ((line=be.readLine())!=null){if ("886".equals(line)){break;}bw.write(line);bw.newLine();bw.flush();}//釋放資源s.close();} }

    練習3

    • 客戶端:數(shù)據(jù)來自于鍵盤錄入,直到輸入的數(shù)據(jù)是886,發(fā)送數(shù)據(jù)結(jié)束
    • 服務(wù)器:接收到的數(shù)據(jù)寫入文本文件
    //服務(wù)器端 package TPracticeDemo_03;import com.sun.source.tree.WhileLoopTree;import java.io.*; import java.net.ServerSocket; import java.net.Socket;//- 服務(wù)器:接收到的數(shù)據(jù)寫入文本文件 public class ServerDemo {public static void main(String[] args) throws IOException {//創(chuàng)建服務(wù)器Socket對象(ServerSocket)ServerSocket ss = new ServerSocket(10010);//監(jiān)聽客戶端連接,返回Socket對象Socket s = ss.accept();//創(chuàng)建輸入流,接收數(shù)據(jù)BufferedReader data = new BufferedReader(new InputStreamReader(s.getInputStream()));//創(chuàng)建輸出流對象BufferedWriter bw = new BufferedWriter(new FileWriter("myInet\\java.txt"));String line;while ((line = data.readLine()) != null) {bw.write(line);bw.newLine();bw.flush();}//釋放資源ss.close();bw.close();} }//客戶端 package TPracticeDemo_03;import java.io.*; import java.net.InetAddress; import java.net.Socket;//- 客戶端:數(shù)據(jù)來自于鍵盤錄入,直到輸入的數(shù)據(jù)是886,發(fā)送數(shù)據(jù)結(jié)束public class ClientDemo {public static void main(String[] args) throws IOException {//創(chuàng)建客戶端Socket對象Socket s = new Socket(InetAddress.getByName("192.168.31.81"),10010);//創(chuàng)建輸入流,鍵盤錄入數(shù)據(jù),直到886結(jié)束BufferedReader br = new BufferedReader(new InputStreamReader(System.in));//創(chuàng)建輸出流BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));String line;while ((line = br.readLine()) != null) {if ("886".equals(line)) {break;}bw.write(line);bw.newLine();bw.flush();}//釋放資源s.close();} }

    練習4

    • 客戶端:數(shù)據(jù)來自于文本文件
    • 服務(wù)器:接收到的數(shù)據(jù)寫入文本文件
    //服務(wù)器端 package TPracticeDemo_04;import java.io.*; import java.net.ServerSocket; import java.net.Socket;//- 服務(wù)器:接收到的數(shù)據(jù)寫入文本文件 public class ServerDemo {public static void main(String[] args) throws Exception{//創(chuàng)建服務(wù)器端Socket對象ServerSocket ss = new ServerSocket(10010);//監(jiān)聽客戶端連接,返回Socket對象Socket s = ss.accept();//創(chuàng)建輸入流接收數(shù)據(jù)BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));//創(chuàng)建輸出流BufferedWriter bw = new BufferedWriter(new FileWriter("myInet\\java.txt"));String line;while ((line = br.readLine()) != null) {bw.write(line);bw.newLine();bw.flush();}//釋放資源ss.close();bw.close();} }//客戶端 package TPracticeDemo_04;import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.OutputStreamWriter; import java.net.Socket;//- 客戶端:數(shù)據(jù)來自于文本文件public class ClientDemo {public static void main(String[] args) throws Exception{//創(chuàng)建客戶端Socket對象Socket s = new Socket("192.168.31.81",10010);//創(chuàng)建輸入流BufferedReader br = new BufferedReader(new FileReader("myInet\\copy.txt"));//創(chuàng)建輸出流BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));String line;while ((line = br.readLine()) != null) {bw.write(line);bw.newLine();bw.flush();}//釋放資源s.close();br.close();} }

    練習5

    • 客戶端:數(shù)據(jù)來自于文本文件,接收服務(wù)器反饋
    • 服務(wù)器:接收到的數(shù)據(jù)寫入文本文件,給出反饋

    出現(xiàn)問題:程序一直等待

    原因:讀數(shù)據(jù)的方法是阻塞式的
    解決辦法:自定義結(jié)束標記使用shutdownOutput()方法(推薦)

    //服務(wù)器端 package TPracticeDemo_05;import java.io.*; import java.net.ServerSocket; import java.net.Socket;//- 服務(wù)器:接收到的數(shù)據(jù)寫入文本文件,給出反饋 public class ServerDemo {public static void main(String[] args) throws Exception{//創(chuàng)建服務(wù)端Socket對象ServerSocket ss = new ServerSocket(10010);//監(jiān)聽客戶端,返回Socket對象Socket s = ss.accept();//創(chuàng)建輸入流BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));//創(chuàng)建輸出流BufferedWriter bw= new BufferedWriter(new FileWriter("myInet\\copy.txt"));String line;while ((line = br.readLine()) != null) {bw.write(line);bw.newLine();bw.flush();}System.out.println(11111);//創(chuàng)建輸出流輸出反饋數(shù)據(jù)OutputStream outputStream = s.getOutputStream();outputStream.write("數(shù)據(jù)已接收".getBytes());//釋放資源ss.close();bw.close();} }//客戶端 package TPracticeDemo_05;import java.io.*; import java.net.InetAddress; import java.net.Socket;//- 客戶端:數(shù)據(jù)來自于文本文件,接收服務(wù)器反饋public class ClientDemo {public static void main(String[] args) throws Exception{//創(chuàng)建客戶端Socket對象Socket s = new Socket("192.168.31.81", 10010);//創(chuàng)建輸入流BufferedReader br = new BufferedReader(new FileReader("myInet\\java.txt"));//創(chuàng)建輸出流BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));String line;while ((line = br.readLine()) != null) {bw.write(line);bw.newLine();bw.flush();}//void shutdownOutput()禁用此套接字的輸出流s.shutdownOutput();//創(chuàng)建輸入流接收服務(wù)器反饋InputStream inputStream = s.getInputStream();byte[] bys = new byte[1024];int len;while ((len=inputStream.read(bys))!=-1){System.out.println(new String(bys,0,len));}//釋放資源s.close();br.close();} }

    練習6

    • 客戶端:數(shù)據(jù)來自于文本文件,接收服務(wù)器反饋
    • 服務(wù)器:接收到的數(shù)據(jù)寫入文本文件,給出反饋,代碼用線程進行封裝,為每一個客戶端開啟一個線程
    //1.客戶端 package TPracticeDemo_06; //- 客戶端:數(shù)據(jù)來自于文本文件,接收服務(wù)器反饋import java.io.*; import java.net.Socket;public class ClientDemo {public static void main(String[] args) throws Exception{//創(chuàng)建客戶端Socket對象Socket s = new Socket("192.168.31.81",10010);//創(chuàng)建輸入流BufferedReader br = new BufferedReader(new FileReader("myInet\\java.txt"));//創(chuàng)建輸出流BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));String line;while ((line = br.readLine()) != null) {bw.write(line);bw.newLine();bw.flush();}s.shutdownOutput();//創(chuàng)建輸入流接受服務(wù)器反饋InputStream inputStream = s.getInputStream();byte[] bys = new byte[1024];int len;while ((len = inputStream.read(bys)) != -1) {System.out.println(new String(bys,0,len));}//釋放資源s.close();br.close();} }//2.服務(wù)器端 package TPracticeDemo_06;import java.net.ServerSocket; import java.net.Socket;//- 服務(wù)器:接收到的數(shù)據(jù)寫入文本文件,給出反饋,代碼用線程進行封裝,為每一個客戶端開啟一個線程 public class ServerDemo {public static void main(String[] args) throws Exception {//創(chuàng)建服務(wù)器Socket對象ServerSocket ss = new ServerSocket(10010);while (true) {//監(jiān)聽客戶端連接,返回socket對象Socket s = ss.accept();//為每一個客戶端開啟一個線程new Thread(new ServerThread(s)).start();}} }//3.多線程 package TPracticeDemo_06;import java.io.*; import java.net.Socket;public class ServerThread implements Runnable {private Socket s;public ServerThread(Socket s) {this.s = s;}@Overridepublic void run() {//接收數(shù)據(jù)寫到文本文件try {BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));//解決命名沖突問題int count = 0;File file = new File("myInet\\copy"+count+".txt");while (file.exists()) {count++;file = new File("myInet\\copy"+count+".txt");}BufferedWriter bw= new BufferedWriter(new FileWriter(file));String line;while ((line = br.readLine()) != null) {bw.write(line);bw.newLine();bw.flush();}//給出反饋OutputStream outputStream = s.getOutputStream();outputStream.write("服務(wù)器已成功接收數(shù)據(jù)".getBytes());} catch (Exception e){e.printStackTrace();}} }

    commons-io工具類的使用

    commons-io概述

    • commons-io是apache開源基金組織提供的一組有關(guān)IO操作的類庫,可以挺提高lO功能開發(fā)的效率
    • commons-io工具包提供了很多有關(guān)IO操作的類。有兩個主要的類FileUtils, IOUtils

    FileUtils主要有如下方法:

    方法名說明
    String readFileToString(File file, String encoding)讀取文件中的數(shù)據(jù),返回字符串
    void copyFile(File srcFile, File destFile)復制文件
    void copyDirectoryToDirectory(File srcDir, File destDir)復制文件夾

    commons-io使用步驟:

  • 導入commons-io-2.6.jar
  • 在項目中創(chuàng)建一個文件夾:lib
  • 將commons-io-2.6.jar文件復制到lib文件夾
  • 在jar文件上點右鍵,選擇Add as Library->點擊OK
  • 在類中導包使用
  • package com.itheima.demo08_commons_io使用;import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils;import java.io.File; import java.io.IOException;public class Demo08 {public static void main(String[] args) throws IOException {// 1.讀取文件中內(nèi)容返回字符串。參數(shù)1:文件對象,參數(shù)2:字符集String s = FileUtils.readFileToString(new File("day_12/src/com\\itheima\\demo08_commons_io使用\\Demo08.java"), "utf8");System.out.println(s);// 2.復制文件。參數(shù)1:源文件,參數(shù)2:目標文件FileUtils.copyFile(new File("day_12/src/com\\itheima\\demo08_commons_io使用\\Demo08.java"),new File("D:/temp/temp.txt"));// 3.復制文件夾。參數(shù)1:源目錄,參數(shù)2:目標目錄 (連同子孫目錄下的文件和目錄一起復制)FileUtils.copyDirectoryToDirectory(new File("day_12/src/com/itheima"),new File("D:/temp"));} }

    更多內(nèi)容請訪問博主博客:逸樂的博客 - 今晚一起吃火鍋

    文章如有紕漏請指出,整理不易多多包涵。

    Java后續(xù)筆記將持續(xù)更新…

    總結(jié)

    以上是生活随笔為你收集整理的Java笔记 - 黑马程序员_07(多线程,线程同步,线程池,网络编程入门,UDP通信原理,TCP通信原理,commons-io工具类)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。