java知識擴(kuò)展

jvm進(jìn)程

JVM進(jìn)程可以由bin\jps查看。在命令行下輸入jps

  1. 由一個(gè)jdk文件系統(tǒng),可以產(chǎn)生很多jvm進(jìn)程(沒bin\java.exe執(zhí)行后產(chǎn)生一個(gè))
  2. JVM進(jìn)程有個(gè)pid,默認(rèn)情況下河他執(zhí)行的main所在的類相同
  3. bin\jconsole 可以監(jiān)視和管理java程序
  4. jvm的三大財(cái)產(chǎn)
  • 內(nèi)存
    內(nèi)存是JVM擁有的主要財(cái)產(chǎn)之一,內(nèi)存你看到了分堆和非堆,這兩個(gè)值是 在執(zhí)行命令java.exe是可以修改的,如
    java.exe 類名 -Xmx3550m -Xms3550m -XX:MaxPermSize=16m
    -Xmx堆內(nèi)存最大
    -Xms堆內(nèi)存最小
    -XX:MaxPermSize 非堆 (放class,static var)

一般在實(shí)際的過程中將Xms與 Xmx設(shè)置為一樣。應(yīng)為避免程序執(zhí)行后期內(nèi)存不夠或分配不及時(shí)。這兩個(gè)值的大小將直接影響程序的性能<br />

在eclipse中,可以在點(diǎn)Run configurations...后界面設(shè)置.

  • 線程
    程啟動運(yùn)行時(shí)會有一個(gè)線程去啟動main方法
    除了main線程,其它都是JVM內(nèi)置的,我們自己沒有開啟.在實(shí)際運(yùn)行中,這里的線程太多和太少都不行,要維持在一個(gè)合理的范圍,而且也要時(shí)刻關(guān)注他們的狀態(tài)。如果程序中一個(gè)線程都沒有,那么進(jìn)程也死了。


  • 其實(shí)就是程序,包括JRE中的類,使用的第三方的jar
    包和我們應(yīng)用中自己寫的程序,這些類加載進(jìn)入內(nèi)存,都放在非堆中。
    所以我們JVM進(jìn)程是一個(gè)有身份證(pid),有姓名(名稱),擁有內(nèi)存,程類(程序)的一個(gè)靜態(tài)實(shí)體(CPU無法調(diào)度執(zhí)行)。

java類加載器基本知識

java web server 基本實(shí)現(xiàn)原理

  1. 遠(yuǎn)端服務(wù)器使用ServerSocket技術(shù)打開一個(gè)端口,等待請求的到來。
  2. 瀏覽器得到用戶輸入的地址(url)或者得到點(diǎn)擊聯(lián)接地址(url)。
  3. 瀏覽器看輸入的是IP還是域名,如果是域名,通過查找DNS服務(wù)找到此域名IP,并緩存到瀏覽器中,以加快下次查找速度。
  4. 瀏覽器組裝滿足HTTP協(xié)議的數(shù)據(jù)包(請求報(bào)文)。
  5. 瀏覽器請求在本機(jī)隨機(jī)開啟一個(gè)端口與服務(wù)端IP和服務(wù)端端口建立聯(lián)接 (TCP)。(本機(jī)IP + 本機(jī)端口 + 服務(wù)端IP + 服務(wù)端端口,用來唯一標(biāo)示一條TCP 聯(lián)接)
  6. 通過此聯(lián)接發(fā)送HTTP數(shù)據(jù)包。
  7. 服務(wù)端通過端口接收到數(shù)據(jù)之后,解析HTTP數(shù)據(jù)包,組裝成良好的格式,并調(diào)用程序處理。
  8. 服務(wù)程序處理完成之后,服務(wù)端組裝滿足HTTP協(xié)議的數(shù)據(jù)包(響應(yīng)報(bào)文)通過TCP聯(lián)接返回到請求端口上。
  9. 瀏覽器從請求端口得到數(shù)據(jù)解析響應(yīng)報(bào)文得到相應(yīng)數(shù)據(jù)后給瀏覽器軟件進(jìn)行解析渲染。
  10. 請求關(guān)閉聯(lián)接

先用socket技術(shù)實(shí)現(xiàn)一個(gè)main方法

public class WebServer {

    //服務(wù)端Socket只要一個(gè),所以定義成static, 同一時(shí)間只能一個(gè)線程訪問(主線程)
    private static ServerSocket ss;

    public static void main(String[] args) throws IOException {
        //綁定端口,只會執(zhí)行一次
        ss = new ServerSocket(8999);
        //主線程永遠(yuǎn)不死,所以用了個(gè)死循環(huán)
        while (true) {
            //這是一個(gè)IO阻塞式語句,也就是如果沒有請求(IO操作)進(jìn)來,當(dāng)前線程會在此等待,不再向下執(zhí)行
            Socket socket = ss.accept();


            //得到請求報(bào)文(內(nèi)容)
            StringBuffer sb = new StringBuffer();
            PrintWriter pw = null;
            try {
                InputStream socketIn = socket.getInputStream();
                BufferedReader br = new BufferedReader(new InputStreamReader(socketIn));
                while(true) {
                    String msg = br.readLine();
                    sb.append(msg);
                    System.out.println(msg);
                    if (msg == null || msg.trim().equals("")) {
                        break;
                    }
                }
                
                //輸入響應(yīng)報(bào)文
                pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));

                pw.println("HTTP/1.1 200 OK");
                pw.println("Content-Type: text/html;charset=UTF-8");
                pw.println();   //如果注釋掉這句,下面的html不會打印出來,未出頭部

                pw.write("html");
                pw.flush();

            } catch (IOException exception) {
                //TODO 處理異常
            } finally {
                socket.close();
                pw.close();
                //socket = null;
            }
        }
    }
}

啟動程序后,在瀏覽器輸入http://127.0.0.1:8999/abc查看報(bào)文。

瀏覽器中的報(bào)文

但是上程序有一個(gè)問題,一個(gè)線程同一時(shí)間只能運(yùn)行一段代碼,在上面的例子中,邏輯處理是在主線程中運(yùn)行的(當(dāng)產(chǎn)生一個(gè)JVM進(jìn)程時(shí),同時(shí)會產(chǎn)生一個(gè)主線程,main方法中的代碼就是在主線程中執(zhí)行),當(dāng)一個(gè)請求還在處理邏輯和輸出時(shí),此時(shí)又來了一個(gè)請求,那么此請求將會被阻塞。所以我們可以把程序調(diào)整成如下樣子。

public class WebServer {

    //服務(wù)端Socket只要一個(gè),所以定義成static, 同一時(shí)間只能一個(gè)線程訪問
    private static ServerSocket ss;

    public static void main(String[] args) throws IOException {
        ss = new ServerSocket(8999);
        //有線程永遠(yuǎn)不死,所以用了個(gè)死循環(huán)
        while (true) {
            Socket socket = ss.accept();
            Thread thread = new Thread(new Handler(socket));
            thread.start();
        }
    }
}

public class Handler implements Runnable  {

    private Socket socket;
    public Handler(Socket socket){
        this.socket=socket;

    }
    @Override
    public void run() {
        StringBuffer sb = new StringBuffer();
        PrintWriter pw = null;
        try {
            InputStream socketIn = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(socketIn));
            while(true) {
                String msg = br.readLine();
                sb.append(msg);
                System.out.println(msg);
                if (msg == null || msg.trim().equals("")) {
                    break;
                }
            }
            pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));

            pw.println("HTTP/1.1 200 OK");
            pw.println("Content-Type: text/html;charset=UTF-8");
            pw.println();

            pw.write("html");
            pw.flush();

        } catch (IOException exception) {
            //TODO 處理異常
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            pw.close();
            //socket = null;
        }
    }
}

這中間的main方法相當(dāng)于一個(gè)公司的前臺,將客戶(請求) 接入到公司,安排其他人來處理。
這樣每個(gè)請求一個(gè)線程,運(yùn)行完成后,線程就死了,這樣主線程負(fù)擔(dān)就輕了,不會發(fā)生阻塞了,但是新問題又來了,每個(gè)請求一個(gè)線程,那線程太多了,所以我們真正應(yīng)該使用線程池。

servlet注意點(diǎn)

  • servlet可以在web配置中設(shè)置容器一啟動就被創(chuàng)建、初始化而不是第一個(gè)訪問后再被創(chuàng)建、初始化
    <load-on-startup>number(1or2or3)</load-on-startup>數(shù)字越小越早被創(chuàng)建、初始化

  • init(servletconfig) 可以從web.xml中獲取初始化數(shù)值。

  • servlet 中變量要定義在方法內(nèi),不允許放在servlet 下,防止線程串行,線程就不安全(如果這樣,會產(chǎn)生用戶a的線程進(jìn)行到一半,用戶b的線程進(jìn)來將用戶a的信息替換,用戶a有可能登陸到用戶b的賬號)
    無狀態(tài)的對象(只有方法或變量為只讀)可以放在servlet第一層下。
    有狀態(tài)的對象(變量可以改變)注意線程安全問題。

線程注意點(diǎn)

  1. 線程、進(jìn)程、程序之間的關(guān)系
    CPU是所有進(jìn)程共同擁有的,所有進(jìn)程(包括所有JVM進(jìn)程和非JVM進(jìn)程)。大家都知道一個(gè)CPU在一個(gè)時(shí)間點(diǎn),只能運(yùn)行一行指令,也就是我們的程序,在Java中,所有程序都必須運(yùn)行在線程里,所以CPU是調(diào)度線程再運(yùn)行線程中的指令(程序),這些指令在運(yùn)行時(shí)會向它的進(jìn)程申請使用公共財(cái)產(chǎn)(堆內(nèi)存),同時(shí)線程也有自己的私有財(cái)產(chǎn)(棧內(nèi)存),這樣就構(gòu)成了”內(nèi)存(堆)”,”線程(棧內(nèi)存)”,”類(程序)”三者之間的關(guān)系,打個(gè)比方來說:
    一個(gè)家庭有夫妻兩個(gè),他們都有自己的小金庫,同時(shí)也有家庭共同的財(cái)產(chǎn),丈夫在做一件事情時(shí),他要審請家庭財(cái)產(chǎn),同時(shí)要使用自己的小金庫才可以完成。那么在這個(gè)例子中,家庭就是一個(gè)進(jìn)程,夫妻是兩個(gè)線程,共同的財(cái)產(chǎn)是堆內(nèi)存,小金庫是棧內(nèi)存,事情就是類(程序)。夫妻在家庭這個(gè)空間中生存,如果夫妻兩人不幸都死了,那這個(gè)家庭就不存在了(相當(dāng)所有線程死了,進(jìn)程也就死了),但只要有一個(gè)還在,家庭就還在(進(jìn)程中只要有一個(gè)線程還存活,進(jìn)程就還存活)。
  2. 主線程的產(chǎn)生
    啟動一個(gè)JVM進(jìn)程時(shí),JVM會自動為我們創(chuàng)建一個(gè)線程,把它命名成”main”, 并把這個(gè)類中的main方法(程序)放到這個(gè)線程中的run方法中去執(zhí)行。
  3. java中產(chǎn)生線程的方式
    在java編程時(shí),除了main線程是由JVM為我們產(chǎn)生的以外,其它所有線程都由我們自己的程序生成。
    Java中定義線程的方式有兩種,繼承Thread和實(shí)現(xiàn)Runnable接口。我們來看如下程序:
package com.zhengmenbb.thread;
public class ChildThread implements Runnable {
   @Override
   public void run() {
       System.out.println(Thread.currentThread().getName());
       for(long i = 0; i < Long.MAX_VALUE; i++) {
       }
   }
}

上面程序使用Runnable定義了一個(gè)線程類,在主程序(或者其它程序)中我們這樣調(diào)用:

package com.zhengmenbb.thread;
public class TestMain {

   public static void main(String[] args) {
       //System.out.println(Thread.currentThread().getName());
       Thread thread = new Thread(new ChildThread());
       thread.start();
   }
}

運(yùn)行main方法,你會看到生成的線程名字為:Thread-0, 當(dāng)然你可以通過thread這個(gè)線程對象引用來重設(shè)他的名字,優(yōu)先級等。使用jconsole我們打開這個(gè)進(jìn)程的線程tab頁:

Paste_Image.png

會發(fā)現(xiàn)main線程已死,但是Thread-0還活著,因?yàn)槲沂褂昧艘粋€(gè)時(shí)間很長的循環(huán).這也證明了只要一個(gè)線程還活著,進(jìn)程是不會死的, 但如果你等把Thread-0中run方法執(zhí)行完成了,進(jìn)程就死了。記住,只要run方法中的代碼執(zhí)行完成了,線程就死了.


我們再來看線程的另一種定義方法:繼承 Thread

package com.zhengmenbb.thread;
public class ChildThread1 extends Thread {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
        for(long i = 0; i < Long.MAX_VALUE; i++) {

        }
    }
}

在主程序(或者其它程序)中我們這樣調(diào)用:

package com.zhengmenbb.thread;
public class TestMain {

    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName());
        Thread thread = new ChildThread1();
        thread.start();
    }
}

兩種方式只是定義線程類方式不一樣,運(yùn)行時(shí)產(chǎn)生的線程是一樣的,強(qiáng)烈建議使用Runnable接口方式。

  1. ”用戶線程”和“守護(hù)線程”

請看如下代碼:

package com.zhengmenbb.thread;

public class TestMain {

    public static void main(String[] args) {

        Runtime.getRuntime().addShutdownHook(new Thread(){
            @Override
          public void run() {
          System.out.println("JVM 退出了");
          }
        });

        Thread thread = new Thread(new ChildThread());
        thread.setDaemon(true);
        thread.start();
    }
}  

在上面這段代碼中,我把線程的daemon(“守護(hù)”),設(shè)置了true, 你再運(yùn)行這段代碼,發(fā)現(xiàn)JVM進(jìn)程很快退出了。我們知道m(xù)ain線程run方法運(yùn)行很快,所以很快就死了,相當(dāng)妻子死了,Thread-0這個(gè)線程我們設(shè)置了daemon(“守護(hù)”),也就是說妻子死了,丈夫要守護(hù)她,也自殺隨她去了,這樣家庭(JVM)就死了。

那下面我們定義一個(gè)“用戶線程”和“守護(hù)線程”:

“用戶線程”: 只要有一個(gè)這樣的線程還活著,JVM就不會退出,這樣的線程我們定義為用戶線程. 其實(shí)是主線程和我們把daemon(“守護(hù)”),設(shè)置為false的線程。

“守護(hù)線程”:只要沒有用戶線程存活了,我就會自殺,這樣JVM主會退出。只要有用戶線程活著,我也活著。這一類線程我們稱為“守護(hù)線程”, 其實(shí)就是把daemon(“守護(hù)”),設(shè)置為true的線程。

值得一提的是,當(dāng)你在一個(gè)守護(hù)線程中產(chǎn)生了其他線程,那么這些新產(chǎn)生的線程不用設(shè)置Daemon屬性,都將是守護(hù)線程,用戶線程同樣。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,818評論 11 349
  • 一、認(rèn)識多任務(wù)、多進(jìn)程、單線程、多線程 要認(rèn)識多線程就要從操作系統(tǒng)的原理說起。 以前古老的DOS操作系統(tǒng)(V 6....
    GT921閱讀 1,099評論 0 3
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,639評論 19 139
  • 本文主要講了java中多線程的使用方法、線程同步、線程數(shù)據(jù)傳遞、線程狀態(tài)及相應(yīng)的一些線程函數(shù)用法、概述等。 首先講...
    李欣陽閱讀 2,600評論 1 15
  • 為喪失提供的一個(gè)哀傷過程 --------心理咨詢手記 人的成長過程中伴...
    恰如初閱讀 382評論 0 0

友情鏈接更多精彩內(nèi)容