您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關Java中怎么利用ThreadAPI實現多線程,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
1.Thread的構造方法
package threadAPI; public class CreateThread { public static void main(String[] args) { Thread t1 = new Thread(); Thread t2 = new Thread(); t1.start(); t2.start(); System.out.println(t1.getName()); System.out.println(t2.getName()); } }
總結1:
創建線程對象Thread,默認有一個線程名,以Thread-開頭,從0開始計數
Thread-0
Thread-1
Thread-2
可以看到Thread()中默認傳入的第二個參數,即Runnable接口為null
在init方法中,會將我們傳入的target給Thread的成員變量
然后在調用run方法的時候,會做如下判斷
所以當target為null的時候,默認的run方法中什么也不做
總結2:
如果在構造Thread的時候,沒有傳遞Runnable接口或者沒有復寫Thread的run方法,該Thread將不會調用任何東西
如果傳遞了Runnable接口的實例,則會執行該方法的邏輯代碼
如果復寫了Thread的run方法,則會執行復寫的邏輯代碼
為線程傳遞一個線程名
這時我們傳入的參數名,會傳遞給線程對象的成員變量name
為線程傳遞線程名的同時,傳遞Runnbale接口的實現類對象,調用原理同上
我們還可以在為線程傳入線程組
其實在上述的方法中沒有傳入線程組的情況下,init方法的ThreadGroup默認被傳入null
parent即調用Thread對象的start方法的線程
package threadAPI; public class CreateThread { public static void main(String[] args) { Thread t = new Thread(); t.start(); System.out.println(t.getThreadGroup()); System.out.println(Thread.currentThread().getName()); System.out.println(Thread.currentThread().getThreadGroup()); } }
總結:
如果構造線程對象時未傳入ThreadGroup,Thread默認會獲取父線程的ThreadGroup作為該線程的ThreadGroup,此時子線程和父線程在同一個ThreadGroup中
我們可以查看當前ThreadGroup中有多少個線程在運行
package threadAPI; public class CreateThread { public static void main(String[] args) { Thread t = new Thread(); t.start(); ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); System.out.println(threadGroup.activeCount()); //創建一個Thread數組 Thread[] threads = new Thread[threadGroup.activeCount()]; //將threadGroup中的數組枚舉到threads數組中 threadGroup.enumerate(threads); //打印threads接收到的線程 for (Thread thread : threads) { System.out.println(thread); } } }
線程組的詳細介紹見后續博文:
stackSize:
演示代碼:
public class CreateThreadDemo { private static int counter; public static void main(String[] args) { Thread t1 = new Thread(null, new Runnable() { @Override public void run() { try { add(1); }catch (Error e){ System.out.println(counter); e.printStackTrace(); } } private void add(int i){ counter++; add(i+1); } },"",1<<24); t1.start(); } }
運行結果:
將stackSize修改:
從修改為
運行結果:
總結:
構造Thread的時候傳入stacksize代表著該線程占用虛擬機棧的大小(虛擬機棧本身的大小在程序運行時就已經確定),如果沒有指定stacksize的大小,默認是0,0代表著會忽略該參數,該參數會被JNI函數去使用
需要注意的是:該參數有一些平臺有效,在有些平臺則無效2.start方法
2、調用start
方法,會執行run方法
但不能調用start方法兩次,會拋出異常
也可以直接調用Thread的run方法,但不會啟動另一個線程
當你第一次調用線程的start方法時候,會返回兩個線程,一個是調用線程,一個是新創建的執行run方法的線程
start方法的源碼實現使用了模板方法,以下是模擬它的實現技巧:
public class TemplateMethod { //此處final,是因為start方法的邏輯是固定的,不允許子類重寫此方法 public final void start(String message) { System.out.println("################"); run(message); System.out.println("################"); } protected void run(String message) { } public static void main(String[] args) { TemplateMethod t1 = new TemplateMethod() { @Override protected void run(String message) { System.out.println("*"+message+"*"); } }; t1.start("hello,world"); TemplateMethod t2 = new TemplateMethod() { @Override protected void run(String message) { System.out.println("+"+message+"+"); } }; t2.start("hello,world"); } }
上述代碼使用模板方法的大致思想是定義一個模板方法,它其中一些代碼已經實現,而另一些需要交給用戶去上實現,該方法確定結構,對外提供統一的調用接口start,定義另一個方法,提供給用戶繼承,用戶可以重寫,也可以不重寫run方法,即不變的部分用模板實現,變化的部分提取出來,交給用戶繼承重寫
3.setDaemon
Java中的線程分為兩種:
用戶線程(setDaemo(false))
守護線程(setDaemo(true))
什么是守護線程?
專門用于服務其他的線程,如果其他的線程(即用戶線程(包括main線程))都執行完畢,JVM中只剩下守護線程時,此時JVM不管守護線程是否執行完畢,都會結束執行
示例代碼1:
public class DaemonDemo { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(){ @Override public void run() { try { System.out.println(Thread.currentThread().getName()+"\trun"); Thread.sleep(10*1000); System.out.println(Thread.currentThread().getName()+"\tout"); }catch (Exception e){ e.printStackTrace(); } } }; t.setDaemon(true); t.start(); Thread.sleep(5*1000); System.out.println(Thread.currentThread().getName()); } }
當main線程執行結束時,運行的唯一線程是守護線程,JVM退出,并未執行此句
疑問:在我們創建的t線程中創建一個守護線程t1,當t線程結束,主線程未結束的時候,守護線程是否結束呢?
示例代碼2:
/** * t1、t3、main是用戶線程 * t2、t4是守護線程 * * t1執行1秒結束 * main執行5秒結束 * t3執行8秒結束 * * t2、t4正常執行完需要10秒 */ public class DaemonDemo { public static void main(String[] args) throws InterruptedException { //t1線程睡眠1秒就執行結束 Thread t1 = new Thread(){ @Override public void run() { //t2線程是守護線程,如果像用戶線程一樣,必須執行10秒才執行結束 Thread t2 = new Thread(()-> { try { for (int i = 1; i <= 10; i++) { Thread.sleep(1*1000); System.out.println("t2守護線程執行:"+i); } System.out.println("t2守護線程結束"); } catch (InterruptedException e) { e.printStackTrace(); } }); t2.setDaemon(true); t2.start(); try { Thread.sleep(1*1000); System.out.println("t1線程結束"); }catch (Exception e){ e.printStackTrace(); } } }; t1.start(); //t3線程執行8秒執行結束 Thread t3 = new Thread(()-> { try { for (int i = 1; i <= 8; i++) { Thread.sleep(1*1000); System.out.println("t3守護線程執行:"+i); } System.out.println("t3守護線程結束"); } catch (InterruptedException e) { e.printStackTrace(); } }); t3.start(); //t4線程是守護線程,如果像用戶線程一樣,必須執行10秒才執行結束 Thread t4 = new Thread(()-> { try { for (int i = 1; i <= 10; i++) { Thread.sleep(1*1000); System.out.println("t4守護線程執行:"+i); } System.out.println("t4守護線程結束"); } catch (InterruptedException e) { e.printStackTrace(); } }); t4.setDaemon(true); t4.start(); //main線程執行5秒就執行結束 Thread.sleep(5*1000); System.out.println("main線程結束"); } }
總結:只有當JVM中的用戶線程(包括main線程)執行完的時候,未執行完的守護線程會隨著JVM退出而被強制退出,不會再執行后續的代碼
示例代碼3:
public class DaemonDemo { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(){ @Override public void run() { try { Thread.sleep(1*1000); System.out.println("t1線程結束"); }catch (Exception e){ e.printStackTrace(); } } }; t1.start(); t1.setDaemon(true); } }
總結:
setDaemon(true)方法必須在調用start方法之前調用,
否則會拋出java.langIllegalThreadStateException異常
4.獲取線程的名稱、id、優先級
(1)獲取線程的名稱
1.使用Thread類中的方法getName
2.可以獲得當前正在執行的線程,使用線程中的方法getName()獲取線程的名稱
示例代碼:
MyThread.java
package Demo01; //1.創建一個Thread類的子類 public class MyThread extends Thread{ //2.在Thread類的子類中重寫Thread類的run方法,設置線程任務,即線程要干什么 @Override public void run() { //第一種方式:直接調用getName獲取線程名稱 System.out.println(this.getName()); //第二種方式:獲得當前正在執行的線程,使用線程中的方法getName()獲取線程的名稱 //System.out.println(Thread.currentThread().getName()); } }
ThreadDemo01.java
package Demo01; public class ThreadDemo01 { public static void main(String[] args) { //3.創建Thread類的子類對象 MyThread mt=new MyThread(); //4.調用Thread類的start方法,開啟線程,執行run方法 mt.start(); new MyThread().start(); new MyThread().start(); //獲得當前正在執行的線程,使用線程中的方法getName()獲取線程的名稱 System.out.println(Thread.currentThread().getName()); } }
(2)獲取id
public class IdDemo { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ try { Thread.sleep(100*1000); }catch (Exception e){ e.printStackTrace(); } },"t1"); System.out.println(t1.getName()+"線程的線程id為:"+t1.getId()); System.out.println("main線程的id為:"+Thread.currentThread().getId()); } }
在JVM啟動時,除過main線程,JVM還會啟動的9個后臺線程,我們的線程id從11開始遞增
(3)獲取線程優先級
說明:高優先級的線程要搶占低優先級線程CPU的執行權,但是只是從概率上講,高優先級的線程高概率的情況下被執行,并不意味著只有當高優先級的線程執行完以后,低優先級的線程才執行
通過優先級可以企圖改變線程執行的優先順序,但是不一定會按照我們定義的順序去執行,所以不要通過線程優先級去控制先去執行哪個線程再去執行哪個線程
示例代碼:
public class PriorityDemo { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName()+"-Index"+i); } },"t1"); t1.setPriority(Thread.MAX_PRIORITY); Thread t2 = new Thread(()->{ for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName()+"-Index"+i); } },"t2"); t2.setPriority(Thread.NORM_PRIORITY); Thread t3 = new Thread(()->{ for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName()+"-Index"+i); } },"t3"); t3.setPriority(Thread.MIN_PRIORITY); t1.start(); t2.start(); t3.start(); } }
可以看到還是可以看到它們的交替執行,并非一定按照我們賦予的優先級順序來執行
5.join
含義:調用join方法的線程等待執行join方法的線程執行結束(下面的方法是執行join方法的線程固定時間,然后再繼續與存活的其他線程一起交替執行下面的代碼)
示例代碼1:
當沒有join之前
import java.util.stream.IntStream; public class ThreadJoin { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ IntStream.range(1,10) .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i)); },"t1"); t1.start(); IntStream.range(1,10) .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i)); } }
main線程和t1線程交替執行
當在main線程中調用t1線程的join方法時
import java.util.stream.IntStream; public class ThreadJoin { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ IntStream.range(1,10) .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i)); },"t1"); t1.start(); t1.join(); IntStream.range(1,10) .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i)); } }
main線程要等到t1線程執行完,再執行它自己的代碼
示例代碼2:
import java.util.stream.IntStream; public class ThreadJoin { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ IntStream.range(1,10) .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i)); },"t1"); Thread t2 = new Thread(()->{ IntStream.range(1,10) .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i)); },"t2"); t1.start(); t2.start(); t1.join(); t2.join(); IntStream.range(1,10) .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i)); } }
在A線程中調用B線程的join方法,則A等待B線程,與其他線程無關
上述代碼中,在main線程中調用了t1和t2的join方法,所以是main線程等t1和t2執行完了,才繼續執行下面的代碼,但是t1和t2線程之間沒有仍然交替執行
示例代碼3:
import java.util.stream.IntStream; public class ThreadJoin { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(()->{ try { Thread.sleep(1000); IntStream.range(1,10) .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i)); } catch (InterruptedException e) { e.printStackTrace(); } },"t1"); t1.start(); t1.join(1000); IntStream.range(1,10) .forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i)); } }
上述代碼main線程等待了t1線程執行了1秒之后,又繼續與t1線程交替執行即并發執行
6.Interrupt
6.1 interrupt()和isInterrupted
其作用是中斷此線程(此線程不一定是當前線程,而是指調用該方法的Thread實例所代表的線程),但實際上只是給線程設置一個中斷標志,線程仍會繼續運行。
而當調用wait、sleep、join方法時,就會清除該中斷標志,并且拋出InterruptedException異常,我們可以捕獲該異常,然后做一些事情,比如,break跳出循環繼續向下執行結束程序
只有一個作用:判斷此線程(此線程不一定是當前線程,而是指調用該方法的Thread實例所代表的線程)的線程狀態(即中斷標志是否被設置)
不會清除中斷標志
示例代碼1:
public class InterruptDemo { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(){ @Override public void run() { while(true){ System.out.println("t線程正在執行"); System.out.println(">>"+this.isInterrupted()); if(this.isInterrupted()){ break; } } System.out.println("只是設置一個中斷標志"); System.out.println("中斷標志還在否?"+this.isInterrupted()); } }; t.start(); //簡單進行休眠,保證線程t進入運行狀態 Thread.sleep(100); System.out.println("main\t"+t.isInterrupted()); t.interrupt(); System.out.println("main\t"+t.isInterrupted()); } }
結論:
即使我們調用了interrupt,它并不會結束程序,而是設置一個中斷標志
isInterrupted方法不會清除中斷標志
示例代碼2:
public class InterruptDemo { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(){ @Override public void run() { while(true){ try { Thread.sleep(10); }catch (InterruptedException e){ System.out.println("收到中斷信號"); e.printStackTrace(); } } } }; t.start(); //簡單進行休眠,保證線程t進入運行狀態 Thread.sleep(100); System.out.println("main\t"+t.isInterrupted()); t.interrupt(); System.out.println("main\t"+t.isInterrupted()); } }
此種結果下程序是按照如下順序被調度的:
此種結果下程序是按照如下順序被調度的:
結論:
sleep方法會清除中斷標志并拋出InterruptedException異常
示例代碼3:
public class InterruptDemo { private static final Object MONITTOR = new Object(); public static void main(String[] args) throws InterruptedException { Thread t = new Thread(){ @Override public void run() { while(true){ synchronized (MONITTOR){ try { MONITTOR.wait(10); }catch (InterruptedException e){ System.out.println("收到中斷信號"); e.printStackTrace(); } } } } }; t.start(); //簡單進行休眠,保證線程t進入運行狀態 Thread.sleep(100); System.out.println("main\t"+t.isInterrupted()); t.interrupt(); System.out.println("main\t"+t.isInterrupted()); } }
結論:
wait方法會清除中斷標志并拋出InterruptedException異常
示例代碼4:
public class InterruptDemo { public static void main(String[] args) { Thread t = new Thread(){ @Override public void run() { while(true){ } } }; t.start(); //使用t2線程去執行interrupt()線程t Thread t2 = new Thread(){ @Override public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } t.interrupt(); System.out.println("interrupt"); } }; t2.start(); try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } } }
當我們調用了join方法時,使用interrupt方法設置線程標志,我們發現程序并沒有被打斷,拋出異常,與API描述的不符?
我們反思一下,sleep,wait都是當前線程sleep,wait,而這里調用join的是main線程,即main線程join,而我們中斷的是t線程,所以沒有拋出異常,代碼修改如下:
public class InterruptDemo { public static void main(String[] args) { Thread t = new Thread(){ @Override public void run() { while(true){ } } }; t.start(); Thread main = Thread.currentThread(); //使用t2線程去執行interrupt()線程t Thread t2 = new Thread(){ @Override public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } main.interrupt(); System.out.println("interrupt"); } }; t2.start(); try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } } }
可以看到join的底層還是在調用wait方法
6.2 interrupted()
兩個作用:
判斷當前線程狀態(即中斷標志是否被設置)
清除中斷標志(如果已經被設置)
如果線程被interrupt()方法設置過中斷標志,當執行interrupted()方法就會清除該中斷標志并返回true,就相當于我們接收到了interrupt()方法傳來的“中斷信號”,我們可以通過true或false這樣的判斷,來做一些事情
如果線程中的中斷標志沒有被設置,它就會返回false
有了isInterrupted()方法,為什么還要interrupted()方法呢?
因為如果我們是傳給Thread一個Runnabe接口,重寫其中的run方法,我們又想調用判斷是否有中斷標志,我們又無法調用isInterrupted()方法,所以Thread提供靜態方法interrupted()供我們使用
并且isInterrupted和interrupted一個很大區別就是:interrupted會清除中斷標志,而isInterrupted不會
源碼如下:
供我們調用的isInterrupted:
本地方法isInterrupted:
供我們調用的靜態interrupted:
public class InterruptDemo { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(new Runnable() { @Override public void run() { { while(true){ //此處在Runnable接口中重寫run方法,只能調用靜態方法interrupted判斷是否有中斷標志 if(Thread.interrupted()) { System.out.println("收到中斷信號"); System.out.println("中斷標志還在否?"+Thread.interrupted()); break; } } } } }); t.start(); //簡單進行休眠,保證線程t進入運行狀態 Thread.sleep(100); System.out.println("main\t"+t.isInterrupted()); t.interrupt(); System.out.println("main\t"+t.isInterrupted()); } }
6.3 如何采用優雅的方式結束線程?
(1)方式1:通過開關的方式即一個flag的方式去結束一個線程
/** * 通過開關的方式即一個flag的方式去結束一個線程 */ public class ThreadCloseGraceful { private static class Worker extends Thread{ private volatile boolean on = true; @Override public void run() { while(on){ } } public void shutdown(){ on = false; } } public static void main(String[] args) { Worker worker = new Worker(); worker.start(); try { Thread.sleep(10000); }catch (InterruptedException e){ e.printStackTrace(); } worker.shutdown(); } }
(2)方式2:通過中斷結束一個線程
/** * 通過捕獲中斷異常來結束程序 */ public class ThreadCloseGraceful { private static class Worker extends Thread{ @Override public void run() { while(true){ try { Thread.sleep(1000); //遇到sleep清除中斷狀態,并捕獲拋出的中斷異常 }catch (InterruptedException e){ System.out.println("Worker線程捕獲到了中斷異常"); break; } } //這里還可以實現一些邏輯的代碼 System.out.println("Worker線程被中斷結束"); } } public static void main(String[] args) { Worker worker = new Worker(); worker.start(); try { Thread.sleep(5000); }catch (InterruptedException e){ e.printStackTrace(); } worker.interrupt(); //設置worker線程的中斷狀態 System.out.println("主線程結束"); } }
/** * 通過線程調用靜態方法interrupted來結束程序 */ public class ThreadCloseGraceful { private static class Worker extends Thread{ @Override public void run() { while(true){ if(Thread.interrupted()) //通過調用此方法,清除中斷狀態,清除后返回true break; } //這里還可以實現一些邏輯的代碼 System.out.println("Worker線程被中斷結束"); } } public static void main(String[] args) { Worker worker = new Worker(); worker.start(); try { Thread.sleep(5000); }catch (InterruptedException e){ e.printStackTrace(); } worker.interrupt(); //設置中斷狀態 System.out.println("主線程結束"); } }
(3)方式3:封裝一個類按照時間強制結束一個線程
分析上述兩種方式不能解決的問題:
當我有一個很耗時的任務,本來預期半個小時結束,結果它運行了兩個小時,我想結束它,也就是它在一次循環中一直執行或阻塞,它沒有機會去判斷開關狀態或者中斷標志狀態
方式3的代碼:
public class ThreadService { //定義一個執行線程用于控制守護線程執行我們的業務 private Thread excuteThread; //用于判斷線程是否執行完,執行完的話,關閉方法就不用再去調用中斷方法 private boolean finished = false; public void excute(Runnable task){ excuteThread = new Thread(){ @Override public void run() { //創建一個守護線程 Thread runner = new Thread(task); runner.setDaemon(true); runner.start(); try { runner.join(); finished = true; } catch (InterruptedException e) { } } }; excuteThread.start(); } public void shutdown(long mills){ long currentTime = System.currentTimeMillis(); while (!finished){ if(System.currentTimeMillis() - currentTime > mills){ System.out.println("任務超時,需要結束它"); //我們執行任務的線程由excuteThread線程去join // 那么我們設置中斷標志,異常會被捕獲,然后不執行任何代碼,直接結束執行線程 excuteThread.interrupt(); break; } //業務既沒有完成,也沒有到我們設定的關閉時間,短暫的進行休眠 try { excuteThread.sleep(1); } catch (InterruptedException e) { System.out.println("執行線程被打斷"); break; } } finished = false; } }
/** * 通過守護線程執行業務,通過一個用戶線程控制守護線程 * * 注意:這種方法的前提是JVM中只有用戶線程, * 即當我們調用shutdown以后不能再有其他用戶線程還在執行,有的話,守護線程不會被結束 */ public class ThreadCloseGraceful { public static void main(String[] args) { ThreadService service = new ThreadService(); long start = System.currentTimeMillis(); service.excute(()->{ //假設在執行一個很耗時的任務 while(true){ System.out.println("守護線程執行的時間:"+(System.currentTimeMillis()-start)+"ms"); } }); //等待5000ms結束該線程 service.shutdown(5000); long end = System.currentTimeMillis(); System.out.println(end - start); /** * 此處如果不注釋,則為結果2 * * 因為中斷結束了執行線程,但是main作為用戶線程并沒有結束,所以守護線程并沒有結束 */ /* try { Thread.sleep(10*1000); } catch (InterruptedException e) { e.printStackTrace(); } */ } }
運行結果1:守護線程隨著唯一的excuteThread被結束而結束
運行結果2:由于main線程在excuteThread結束后并未執行完,守護線程未結束
所以注意:
這種方法的前提是JVM中只有一個用戶線程,即當我們調用shutdown以后不能再有其他用戶線程還在執行,有的話,守護線程不會被結束
7.yield
當調用Thread.yield函數時,會給線程調度器一個當前線程愿意讓出CPU使用的暗示,只是大概率下會把CPU的執行權交給其他線程,但是不是絕對的,線程調度器可能會忽略這個暗示,還可能再次把執行權分配給當前線程
yield也不會對鎖的行為有影響
public class YieldDemo { public static void main(String[] args) { Runnable yieldTask = new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+i); if(i == 5){ Thread.yield(); } } } }; Thread t1 = new Thread(yieldTask,"A"); Thread t2 = new Thread(yieldTask,"B"); t1.start(); t2.start(); } }
看完上述內容,你們對Java中怎么利用ThreadAPI實現多線程有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。