Java基礎(八)——字符流、編碼表、線程

目錄

字符流

爲什麼會出現字符流

編碼表

小結:採用何種規則編碼,就要採用對應規則解碼,否則就會出現亂碼

字符流中編碼解碼

字符緩衝流特有功能

IO小結

線程

多線程的實現方式

Class Thread

方式1:繼承Thread類

兩個小問題:

線程調度

線程控制

線程同步

案例:賣票

賣票案例數據安全問題的解決


 

字符流

一個漢字存儲:
如果是GBK編碼,佔用2個字節
如果是UTF- 8編碼,佔用3個字節

 

爲什麼會出現字符流

由於字節流操作中文不是特別的方便,所以ava就提供字符流
字符流=字節流+編碼表
用字節流複製文本文件時,文本文件也會有中文,但是沒有問題,原因是最終底層操作會自動進行字節拼接成
中文,如何識別是中文的呢?

  • 漢字在存儲的時候, 無論選擇哪種編碼存儲,第一個字節都是負數

 

編碼表

基礎知識:

  • 計算機中儲存的信息都是用二 進制數表示的;我們在屏幕上看到的英文、漢字等字符是二進制數轉換之後的結果
  • 按照某種規則,將字符存儲到計算機中,稱爲編碼。反之,將存儲在計算機中的二進制數按照某種規則解析顯示出來,稱爲解碼。這裏強調一下:按照A編碼存儲,必須按照A編碼解析,這樣才能顯示正確的文本符號。否則就會導致亂碼現象

字符編碼:就是一套自然語言的字符與二 進制數之間的對應規則(A.65)

小結:採用何種規則編碼,就要採用對應規則解碼,否則就會出現亂碼

字符流中編碼解碼

字符流抽象基類

  • Reader: 字符輸入流的抽象類
  • Writer: 字符輸出流的抽象類

字符流中和編碼解碼問題相關的兩個類:

  • InputStreamReader
  • OutputStreamWriter

InputStreamReader:是從字節流到字符流的橋粱

  • 它讀取字節,並使用指定的編碼將其解碼爲字符
  • 它使用的字符集可以由名稱指定,也可以被明確指定,或者可以接受平臺的默認字符集

OutputStreamWriter:是從字符流到字節流的橋樑

  • 是從字符流到字節流的橋樑,使用指定的編碼將寫入的字符編碼爲字節
  • 它使用的字符集可以由名稱指定,也可以被明確指定,或者可以接受平臺的默認字符集

代碼:

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

public class ZiFu {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("D:\\eclipse\\API文檔\\File.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos);
        osw.write("中國加油");
        osw.close();
    }
}

字符緩衝流特有功能

BufferedWriter:

  • void newLine():寫- -行行分隔符, 行分隔符字符串由系統屬性定義

BufferedReader:

  • public String readLine(): 讀-行文字。 結果包含行的內容的字符串, 不包括任何行終止字符,如果流的結尾已經到達,則爲null

代碼:

public class ZiFu {
    public static void main(String[] args) throws IOException {
//        //創建字符緩衝輸出流
//        BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\eclipse\\API文檔\\File.txt"));
//        //寫數據
//        for (int i = 0; i < 10; i++) {
//            bw.write("hello" + i);
//            bw.write("\r\n");
//            bw.newLine();
//            bw.flush();
//        }
//        //釋放資源
//        bw.close();
        //創建字符緩衝輸入流
        BufferedReader br = new BufferedReader(new FileReader("D:\\eclipse\\API文檔\\File.txt"));
        String by;
        while ((by=br.readLine())!=null){
            System.out.print(by);
        }
        br.close();

    }
}

結果:

IO小結

 

線程

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

  • 單線程: 一個進程如果只有一條執行路徑, 則稱爲單線程程序
  • 多線程: 一個進程如果有多條執行路徑,則稱爲多線程程序

多線程的實現方式

Class Thread

  • public class Thread
    extends Object
    implements Runnable
    線程是程序中執行的線程。 Java虛擬機允許應用程序同時執行多個執行線程。

    每個線程都有優先權。 具有較高優先級的線程優先於優先級較低的線程執行。 每個線程可能也可能不會被標記爲守護程序。 當在某個線程中運行的代碼創建一個新的Thread對象時,新線程的優先級最初設置爲等於創建線程的優先級,並且當且僅當創建線程是守護進程時纔是守護線程。

    當Java虛擬機啓動時,通常有一個非守護進程線程(通常調用某些指定類的名爲main的方法)。 Java虛擬機將繼續執行線程,直到發生以下任一情況:

    創建一個新的執行線程有兩種方法。 一個是將一個類聲明爲Thread的子類。 這個子類應該重寫run類的方法Thread 。 然後可以分配並啓動子類的實例。

    • 已經調用了Runtime類的exit方法,並且安全管理器已經允許進行退出操作。
    • 所有不是守護進程線程的線程都已經死亡,無論是從調用返回到run方法還是拋出超出run方法的run
  • 另一種方法來創建一個線程是聲明實現類Runnable接口。 那個類然後實現了run方法。 然後可以分配類的實例,在創建Thread時作爲參數傳遞,並啓動。

方式1:繼承Thread類

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

兩個小問題:

  • 爲什麼 要重寫run()方法?
    • 因爲run()是用來封裝被線程執行的代碼
  • run()方法和start()方法的區別?
    • run():封裝線程執行的代碼,直接調用,相當於普通方法的調用
    • start():啓動線程;然後由JVM調用此線程的run()方法

測試代碼:

public class MyThread {
    public static void main(String[] args) {
        //兩個線程
        ThreadDemo my1 = new ThreadDemo();
        ThreadDemo my2 = new ThreadDemo();
        //此時並沒有多線程執行,還是單線程執行
//        my1.run();
//        my2.run();
        //start()  導致此線程開始執行; Java虛擬機調用此線程的run方法。
        my1.start();
        my2.start();
    }
}
/*
定義一個類MyThread繼承Thread類
在MyThread類中重寫run()方法
創建MyThread類的對象
啓動線程
*/
public class ThreadDemo extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName()+ ":" + i);
        }
    }
}

結果:

Thread類中設置和獲取線程名稱的方法

  • void setName(String name):將此線程的名稱更改爲等於參數name
  • String getName():返回此線程的名稱

線程調度

線程有兩種調度模型

  • 分時調度模型:所有線程輪流使用CPU的使用權,平均分配每個線程佔用CPU的時間片
  • 搶佔式調度模型:優先讓優先級高的線程使用CPU,如果線程的優先級相同,那麼會隨機選擇個, 優先級高的線程獲取的CPU時間片相對多- -些

Java使用的是搶佔式調度模型
假如計算機只有一個CPU, 那麼CPU在某一個時刻只能執行一 條指令 ,線程只有得到CPU時間片,也就是使用權,
可以執行指令。所以說多線程程序的執行是有隨機性,因爲誰搶到CPU的使用權是不一定的

public final int getPriority(): 返回此線程的優先級
System . out. println(tp1. getPriority()); 
public final void setPriority(int newPriority): 更改此線程的優先級
tp1. setPriority(10000);

線程優先級最高10,最低1,默認是5

 

線程控制

代碼:線程暫停1S

public class ThreadDemo extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName()+ ":" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class MyThread {
    public static void main(String[] args) {
        //兩個線程
        ThreadDemo my1 = new ThreadDemo();
        ThreadDemo my2 = new ThreadDemo();
        ThreadDemo my3 = new ThreadDemo();

        my1.setName("曹操");
        my2.setName("劉表");
        my3.setName("孫策");

        my1.start();
        my2.start();
        my3.start();
    }
}

結果: 

 

線程同步

案例:賣票

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

思路:
①義一個類SelITicket實現Runnable接口, 面定義一個成員變量: private int tickets= 100;
②在SellTicket類中寫run0方法實現賣票,代碼步驟如下

  • A:判斷票數大於0,就賣票,並告知是哪個窗口賣的
  • B:賣了票之後,總票數要減1
  • C:票沒有了,也可能有人來問,所以這裏用死循環讓賣票的動作一直執行

③定義一個測試類SellTicketDemo, 裏面有main方法,代碼步驟如下

  • A:創建SellTicket類的對象
  • B:創建三個Thread類的對象,把SelITicket對象作爲構造方法的參數,並給出對應的窗口名稱
  • C:啓動線程
package cn.itcast.day8.買票;

public class SellTicket implements Runnable {
    private int tickets = 100;

    @Override
    public void run() {
//        A:判斷票數大於0,就賣票,並告知是哪個窗口賣的
//        B:賣了票之後,總票數要減1
//        C:票沒有了,也可能有人來問,所以這裏用死循環讓賣票的動作一直執行
        //問題:相同的票出現了多次  ---原因:線程執行的隨機性導致的
        boolean flag = true;
        while (flag == true){
            if (tickets > 0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //t1搶到了CPU的執行權,在控制檯輸出,窗口1出售1張票
                //假設t1繼續擁有CPU執行器,就會執行tickets--;操作
                System.out.println(Thread.currentThread().getName()+"正在出售第"+tickets+"張票");
                tickets--;
            }
            else {
                System.out.println("票已經賣完了");
                flag = false;
            }
        }

    }
}
package cn.itcast.day8.買票;

public class SellTicketDemo {
    public static void main(String[] args) {
//        A:創建SellTicket類的對象
//        B:創建三個Thread類的對象,把SelITicket對象作爲構造方法的參數,並給出對應的窗口名稱
//        C:啓動線程
        SellTicket st = new SellTicket();

        Thread t1 = new Thread(st,"窗口1");
        Thread t2 = new Thread(st,"窗口2");
        Thread t3 = new Thread(st,"窗口3");

        t1.start();
        t2.start();
        t3.start();
    }
}

賣票案例數據安全問題的解決

爲什麼出現問題?(這也是我們判斷多線程程序是否會有數據安全問題的標準

  • 是否是多線程環境
  • 是否有共享數據
  • 是否有多條語句操作共享數據

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

  • 基本思想: 讓程序沒有安全問題的環境

怎麼實現呢?

  • 把多條語句操作共享 數據的代碼給鎖起來,讓任意時刻只能有一個線程執行即可
  • java提供了同步代碼塊來實現

 

 

 

 

 

一起學習,一起進步 -.- ,如有錯誤,可以發評論

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章