這篇文章主要介紹了Java如何實(shí)現(xiàn)多線程、線程同步的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇Java如何實(shí)現(xiàn)多線程、線程同步文章都會(huì)有所收獲,下面我們一起來看看吧。
超過10多年行業(yè)經(jīng)驗(yàn),技術(shù)領(lǐng)先,服務(wù)至上的經(jīng)營模式,全靠網(wǎng)絡(luò)和口碑獲得客戶,為自己降低成本,也就是為客戶降低成本。到目前業(yè)務(wù)范圍包括了:成都網(wǎng)站制作、網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè),成都網(wǎng)站推廣,成都網(wǎng)站優(yōu)化,整體網(wǎng)絡(luò)托管,小程序制作,微信開發(fā),成都App制作,同時(shí)也可以讓客戶的網(wǎng)站和網(wǎng)絡(luò)營銷和我們一樣獲得訂單和生意!
進(jìn)程:是正在運(yùn)行的程序
是系統(tǒng)進(jìn)行資源分配和調(diào)用的獨(dú)立單位
每一個(gè)進(jìn)行都有它自己的內(nèi)存空間和系統(tǒng)資源
進(jìn)程的三個(gè)特征
獨(dú)立性:進(jìn)程與進(jìn)程之間是相互獨(dú)立的,彼此有自己獨(dú)立內(nèi)存區(qū)域
動(dòng)態(tài)性:進(jìn)程是運(yùn)行中的程序,要?jiǎng)討B(tài)的占用內(nèi)存,CPU和網(wǎng)絡(luò)等資源
并發(fā)性:CPU會(huì)分時(shí)輪詢切換依次為每個(gè)進(jìn)程服務(wù),因?yàn)榍袚Q的速度非???,給我們的感覺像是在同時(shí)執(zhí)行,這就是并發(fā)性(并發(fā):同一時(shí)刻同時(shí)有多個(gè)在執(zhí)行)
線程:是進(jìn)程中的單個(gè)順序控制流,是一條執(zhí)行路徑
單線程:一個(gè)進(jìn)程只有一條執(zhí)行路徑
多線程:一個(gè)進(jìn)程有多條執(zhí)行路徑
流程:
1、定義一個(gè)MyTread類繼承Tread類
2、在MyTread類中重寫run()
方法
3、創(chuàng)建MyTread類的對象
4、啟動(dòng)線程:void start()
為什么要重寫run()方法?
因?yàn)閞un()是用來封裝被線程執(zhí)行的代碼
run()方法和start()方法的區(qū)別?
run():封裝線程執(zhí)行的代碼,直接調(diào)用,相當(dāng)于普通方法的的調(diào)用
start():啟動(dòng)線程,然后由JVM調(diào)用此線程中的run()方法
范例
MyTread類:
package test;//1、定義一類MyTread繼承Tread類public class MyThread extends Thread{ 2、在MyTread類中重寫run()方法 @Override public void run() { for(int i=0;i<100;i++) { System.out.println(i); } }}
測試類
package test;public class Demo { public static void main(String[] args) { //3、創(chuàng)建MyTread類的對象 MyThread my1 = new MyThread(); MyThread my2 = new MyThread(); //4、啟動(dòng)線程:void start():啟動(dòng)線程,由Java虛擬機(jī)調(diào)用此線程的run()方法 my1.start(); my2.start(); }}
流程:
1、定義一個(gè)MyRunnable類實(shí)現(xiàn)Runnable接口
2、在MyRunnable類中重寫run()
方法
3、創(chuàng)建MyRunnable類的對象
4、創(chuàng)建Tread類的對象,把MyRunnable對象作為構(gòu)造方法的參數(shù)
5、啟動(dòng)線程
好處:
避免了Java單繼承的局限性
適合多個(gè)相同程序的代碼取處理同一個(gè)資源的情況,把線程和程序的代碼、數(shù)據(jù)有效分離,較好地體現(xiàn)了面向?qū)ο蟮脑O(shè)計(jì)理論
package test;public class Demo { public static void main(String[] args) { //3、創(chuàng)建MyRunnable類的對象 MyRunnable mr = new MyRunnable(); //4、創(chuàng)建Tread類的對象,把MyRunnable對象作為構(gòu)造方法的參數(shù)// Thread t1 = new Thread(mr);// Thread t2 = new Thread(mr); //Thread(Runnable target,String name) Thread t1 = new Thread(mr,"高鐵"); Thread t2 = new Thread(mr,"飛機(jī)"); //5、啟動(dòng)線程 t1.start(); t2.start(); }}
Thread類中設(shè)置和獲取線程名稱的方法
方法名 | 說明 |
---|---|
void setName(Stringname) | 將此線程的名稱更改為等于參數(shù)name |
String getName() | 返回此線程的名稱 |
public Thread(String name) | 通過構(gòu)造方法也可以設(shè)置線程名稱 |
public static Thread currentThread() | 返回對當(dāng)前正在執(zhí)行的線程對象的引用(可以返回main()方法中線程) |
public static void sleep(long time) | 讓當(dāng)前線程休眠多少毫秒再繼續(xù)執(zhí)行 |
MyThread類
package test;public class MyThread extends Thread{ //構(gòu)造方法添加線程名稱 public MyThread(){} public MyThread(String name) { super(name); } @Override public void run() { for(int i=0;i<100;i++) { //1,String getName() 返回此線程的名稱 System.out.println(getName()+":"+i); } }}
測試類
package test;public class Demo { public static void main(String[] args) { /* MyThread my1 = new MyThread(); MyThread my2 = new MyThread(); //2,void setName(Stringname) 將此線程的名稱更改為等于參數(shù)name my1.setName("高鐵"); my2.setName("飛機(jī)");*/ //3,通過構(gòu)造方法設(shè)置線程名稱 //需要自己定義的類中提供此帶參構(gòu)造方法,并通過super訪問父類帶參構(gòu)造方法 /*MyThread my1 = new MyThread("高鐵"); MyThread my2 = new MyThread("飛機(jī)"); my1.start(); my2.start();*/ //4,public static Thread currentThread() 返回對當(dāng)前正在執(zhí)行的線程對象的引用(可以返回main()方法中線程) System.out.println(Tread.currentThread().getName()); //main }}
線程有兩種調(diào)度模型
分時(shí)調(diào)度模型:所有線程輪流使用CPU的使用權(quán),平均分配每個(gè)線程占用CPU的時(shí)間片
搶占式調(diào)度模型:優(yōu)先讓優(yōu)先級(jí)高的線程使用CPU,如果線程的優(yōu)先級(jí)相同,那么會(huì)隨機(jī)選擇一個(gè),優(yōu)先級(jí)高的線程獲取的CPU時(shí)間片相對多一些
Java使用的是搶占式調(diào)度模型
假如計(jì)算機(jī)只有一個(gè)CPU, 那么CPU在某一個(gè)時(shí)刻只能執(zhí)行條指令, 線程只有得到CPU時(shí)間片,也就是使用權(quán),才可以執(zhí)行指令。所以說多線程程序的執(zhí)行是有隨機(jī)性,因?yàn)檎l搶到CPU的使用權(quán)是不一定的
Thread類中設(shè)置和獲取線程優(yōu)先級(jí)的方法
方法名 | 說明 |
---|---|
public final int getPriority() [pra????r?ti] | 返回此線程的優(yōu)先級(jí) |
public final void setPriority(int newPriority) | 更改此線程的優(yōu)先級(jí) |
線程默認(rèn)優(yōu)先級(jí)是5;線程優(yōu)先級(jí)范圍是:1-10
線程優(yōu)先級(jí)高僅僅表示線程獲取的CPU時(shí)間的幾率高,但是要在次數(shù)比較多,或者多次運(yùn)行的時(shí)候才能看到你想要的效果
package test;public class Demo { public static void main(String[] args) { ThreadPriority tp1 = new ThreadPriority(); ThreadPriority tp2 = new ThreadPriority(); ThreadPriority tp3 = new ThreadPriority(); tp1.setName("高鐵"); tp2.setName("飛機(jī)"); tp3.setName("汽車"); //1,public final int getPriority() [pra????r?ti] 返回此線程的優(yōu)先級(jí)// System.out.println(tp1.getPriority()); //5// System.out.println(tp2.getPriority()); //5// System.out.println(tp3.getPriority()); //5 //2,public final void setPriority(int newPriority) 更改此線程的優(yōu)先級(jí) System.out.println(Thread.MAX_PRIORITY); //10 System.out.println(Thread.MIN_PRIORITY); //1 System.out.println(Thread.NORM_PRIORITY); //5 //設(shè)置正確優(yōu)先級(jí) tp1.setPriority(5); tp2.setPriority(10); tp3.setPriority(1); tp1.start(); tp2.start(); tp3.start(); }}
方法名 | 說明 |
---|---|
static void sleep(long millis) | 使當(dāng)前正在執(zhí)行的線程停留(暫停執(zhí)行)指定的毫秒數(shù) |
void join() | 等待這個(gè)線程死亡 |
void setDaemon(boolean on) [?di?m?n] | 將此線程標(biāo)記為守護(hù)線程,當(dāng)運(yùn)行的線程都是守護(hù)線程時(shí),Java虛擬機(jī)很快將退出 (并不是立刻退出) |
案例:sleep()方法
線程類
package test;public class ThreadSleep extends Thread{ @Override public void run() { for(int i=0;i<10;i++) { System.out.println(getName()+":"+i); //1,static void sleep(long millis) 使當(dāng)前正在執(zhí)行的線程停留(暫停執(zhí)行)指定的毫秒數(shù) try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }}
測試類
package test;public class Demo { public static void main(String[] args) { ThreadSleep ts1 = new ThreadSleep(); ThreadSleep ts2 = new ThreadSleep(); ThreadSleep ts3 = new ThreadSleep(); ts1.setName("曹操"); ts2.setName("劉備"); ts3.setName("孫權(quán)"); ts1.start(); ts2.start(); ts3.start();// 曹操:0// 孫權(quán):0// 劉備:0// 孫權(quán):1// 曹操:1// 劉備:1// ... }}
案例:join()方法
package test;public class Demo { public static void main(String[] args) { ThreadJoin tj1 = new ThreadJoin(); ThreadJoin tj2 = new ThreadJoin(); ThreadJoin tj3 = new ThreadJoin(); tj1.setName("康熙"); tj2.setName("四阿哥"); tj3.setName("八阿哥"); tj1.start(); //2,void join() 等待這個(gè)線程死亡 try { tj1.join(); } catch (InterruptedException e) { e.printStackTrace(); } tj2.start(); tj3.start();// 康熙:0// 康熙:1// 康熙:2// 四阿哥:0// 四阿哥:1// 八阿哥:0// 八阿哥:1// 八阿哥:2// 四阿哥:2// ... }}
案例:setDaemon()方法
package test;public class Demo { public static void main(String[] args) { ThreadJoin tj1 = new ThreadJoin(); ThreadJoin tj2 = new ThreadJoin(); ThreadJoin tj3 = new ThreadJoin(); tj2.setName("關(guān)羽"); tj3.setName("張飛"); //設(shè)置主線程為劉備 Thread.currentThread().setName("劉備"); //3,void setDaemon(boolean on) 將此線程標(biāo)記為守護(hù)線程,當(dāng)運(yùn)行的線程都是守護(hù)線程時(shí),Java虛擬機(jī)將退出 tj1.setDaemon(true); tj2.setDaemon(true); tj1.start(); tj2.start(); for(int i=0;i<2;i++) { System.out.println(Thread.currentThread().getName()+":"+i); } //劉備執(zhí)行完后,關(guān)羽和張飛會(huì)很快結(jié)束 }}
為什么出現(xiàn)問題?(這也是我們判斷多線程程序是否會(huì)有數(shù)據(jù)安全問題的標(biāo)準(zhǔn))
是否是多線程環(huán)境
是否有共享數(shù)據(jù)
是否有多條語句操作共享數(shù)據(jù)
如何解決多線程安全問題呢?
基本思想:讓程序沒有安全問題的環(huán)境
怎么實(shí)現(xiàn)呢?
把多條語句操作共享 數(shù)據(jù)的代碼給鎖起來,讓任意時(shí)刻只能有一一個(gè)線程執(zhí)行即可
Java提供 了同步代碼塊的方式來解決
鎖多條語句操作共享數(shù)據(jù),可以使用同步代碼塊實(shí)現(xiàn)
格式
synchronized(任意對象) { 多條語句操作共享數(shù)據(jù)的代碼}
好處:讓多個(gè)線程實(shí)現(xiàn)先后依次訪問共享資源,解決了多線程的數(shù)據(jù)安全問題
弊端:當(dāng)線程很多的時(shí)候,因?yàn)槊總€(gè)線程都會(huì)去判斷同步上的鎖,這是很消耗資源的,無形中降低程序的運(yùn)行效率
sellTicket類
package test;//1,定義一個(gè)類SellTicket實(shí)現(xiàn)Runnable接口,里面定義一個(gè)成員變量: private int tickets= 100;public class SellTicket implements Runnable{ private int tickets = 100; private Object obj = new Object(); //2,在ellTicket類中重寫run0方法實(shí)現(xiàn)賣票, 代碼步驟如下 @Override public void run() { while(true) { //tickes=100 //t1,t2,t3 //假設(shè)t1搶到CPU執(zhí)行器 synchronized (obj){ //t1進(jìn)來后把代碼鎖起來了 if (tickets > 0) { try { Thread.sleep(100); //t1休息100毫秒 } catch (InterruptedException e) { e.printStackTrace(); } //窗口1正在出售第100張票 System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "張票"); tickets--; //tickets=99 } //t1出來了,鎖就被釋放了 } } }}
測試類
package test;public class SellTicketDemo { public static void main(String[] args) { //創(chuàng)建SellTicket類的對象 SellTicket st = new SellTicket(); //創(chuàng)建三個(gè)Thread類的對象,把SellTicket對象作為構(gòu)造方法的參數(shù),并給出對應(yīng)的窗口名稱 Thread t1 = new Thread(st,"窗口1"); Thread t2 = new Thread(st,"窗口2"); Thread t3 = new Thread(st,"窗口3"); //啟動(dòng)線程 t1.start(); t2.start(); t3.start(); }}
作用:把出現(xiàn)線程安全問題的核心方法給鎖起來,每次只能一個(gè)線程進(jìn)入訪問,其他線程必須在方法外面等待
同步方法:就是把synchronized關(guān)鍵字加到方法上;鎖對象為:this
格式:修飾符 synchronized 返回值類型 方法名(方法參數(shù)) {}
同步靜態(tài)方法:就是把synchronized關(guān)鍵字加到靜態(tài)方法上面;鎖對象為:類名.class
格式:修飾符 static synchronized 返回值類型 方法名(方法參數(shù)) {}
package test;public class SellTicket implements Runnable{//1非靜態(tài) private int tickets = 100; private static int tickets = 100; private Object obj = new Object(); private int x = 0; @Override public void run() { while(true) { if(x%2==0) {//1非靜態(tài) synchronized (this) { synchronized (SellTicket.class) { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "張票"); tickets--; //tickets=99 } } } else {// synchronized (obj) {// if (tickets > 0) {// try {// Thread.sleep(100);// } catch (InterruptedException e) {// e.printStackTrace();// }// System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "張票");// tickets--; //tickets=99// }// } sellTicket(); } x++; } }//1非靜態(tài)// private synchronized void sellTicket() {// if (tickets > 0) {// try {// Thread.sleep(100);// } catch (InterruptedException e) {// e.printStackTrace();// }// System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "張票");// tickets--; //tickets=99// }// } private static synchronized void sellTicket() { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "張票"); tickets--; //tickets=99 } }}
源碼中方法都被synchronized修飾
StringBuffer
線程安全, 可變的字符序列
從版本JDK 5開始,被StringBuilder替代。通常應(yīng)該使用StringBuilder類, 因?yàn)樗С炙邢嗤牟僮?但它更快,因?yàn)樗粓?zhí)行同步
Vector
從Java 2平臺(tái)v1.2開始,該類改進(jìn)了List接口, 使其成為Java Collections Framework的成員。 與新的集合實(shí)現(xiàn)不同,Vector被同步。 如果不需要線程安全的實(shí)現(xiàn),建議使用ArrayList代替Vector
Hashtable
該類實(shí)現(xiàn)了一個(gè)哈希表,它將鍵映射到值。任何非null對象都可以用作鍵或者值
從Java 2平臺(tái)v1.2開始,該類進(jìn)行了改進(jìn),實(shí)現(xiàn)了Map接口,使其成為Java Collections Framework的成員。與新的集合實(shí)現(xiàn)不同,Hashtable被同步。 如果不需要線程安全的實(shí)現(xiàn),建議使用HashMap代替Hashtable
Collections
類中static <T> List<T> snchronizedList(List<T> list)
:返回由指定列表支持的同步(線程安全)的列表
package test;import java.util.ArrayList;import java.util.Collection;import java.util.Collections;public class Demo { public static void main(String[] args) { //static <T> List<T> snchronizedList(List<T> list):返回由指定列表支持的同步(線程安全)的列表 Collection<String> list = Collections.synchronizedList(new ArrayList<String>()); /*源碼都是返回Synchronized public static <T> List<T> synchronizedList(List<T> list) { return (list instanceof RandomAccess ? new Collections.SynchronizedRandomAccessList<>(list) : new Collections.SynchronizedList<>(list)); }*/ }}
Lock是接口不能直接實(shí)例化,采用實(shí)現(xiàn)類ReentrantLock來實(shí)例化(JDK5以后)
ReentrantLock構(gòu)造方法:
方法名 | 說明 |
---|---|
ReentrantLock() | 創(chuàng)建一個(gè)ReentrantLock的實(shí)例對象 |
Lock中獲得鎖和釋放鎖方法:
方法名 | 說明 |
---|---|
void lock() | 獲得鎖 |
void unlock() | 釋放鎖 |
推薦使用try{} finall{}
代碼塊來加鎖和釋放鎖
package test;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class SellTicket implements Runnable{ private static int tickets = 100; private Lock lock = new ReentrantLock(); @Override public 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(); } } }}
線程通信一定是多個(gè)線程在操作同一個(gè)資源才需要通信
方法名 | 說明 |
---|---|
public void wait() | 讓當(dāng)前線程進(jìn)入到等待狀態(tài),此方法必須鎖對象調(diào)用 |
public void notify() | 喚醒當(dāng)前鎖對象上等待狀態(tài)的某個(gè)線程,此方法必須鎖對象調(diào)用 |
public void notifyAll() | 喚醒當(dāng)前鎖對象上等待狀態(tài)的全部線程,此方法必須鎖對象調(diào)用 |
為了體現(xiàn)生產(chǎn)和消費(fèi)過程中的等待和喚醒,Java就提供了幾個(gè)方法供我們使用,這幾個(gè)方法在Object類中
Object類的等待和喚醒方法
方法名 | 說明 |
---|---|
void wait() | 導(dǎo)致當(dāng)前線程等待,直到另一個(gè)線程調(diào)用該對象的 notify() 方法或 notifyAll() 方法 |
void notify() | 喚醒正在等待對象監(jiān)視器的單個(gè)線程 |
void notifyAll() | 喚醒正在等待對象監(jiān)視器的所有線程 |
奶箱類
package test;//1:定義奶箱類public class Box { //定義一個(gè)成員變量,表示第x瓶奶 private int milk; //定義一個(gè)成員變量表示奶箱的狀態(tài) private boolean state = false; //提供存儲(chǔ)牛奶和獲取牛奶的操作 public synchronized void put(int milk) { //如果有牛奶等待消費(fèi) if(state) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果沒有牛奶,就生產(chǎn)牛奶 this.milk = milk; System.out.println("送奶工將第" + this.milk + "瓶奶放入奶箱"); //生產(chǎn)完畢后,修改奶箱狀態(tài) state = true; //喚醒其他等待線程 notifyAll(); } public synchronized void get() { //如果沒有牛奶,就等到生產(chǎn) if(!state) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果有牛奶,就消費(fèi)牛奶 System.out.println("用戶拿到第" + this.milk + "瓶奶"); //消費(fèi)完畢后,修改奶箱狀態(tài) state = false; //喚醒其他等待線程 notifyAll(); }}
生產(chǎn)者類
package test;//2:生產(chǎn)者類(Producer):實(shí)現(xiàn)Runnable接口public class Producer implements Runnable { private Box b; public Producer(Box b) { this.b = b; } //重寫run()方法,調(diào)用存儲(chǔ)牛奶的操作 @Override public void run() { for (int i = 1; i <= 5; i++) { b.put(i); } }}
消費(fèi)者類
package test;//3:消費(fèi)者類(Customer);實(shí)現(xiàn)Runnable接口public class Customer implements Runnable{ private Box b; public Customer(Box b) { this.b = b; } //重寫run()方法,調(diào)用獲取牛奶的操作 @Override public void run() { while(true) { b.get(); } }}
測試類
package test;public class BoxDemo { public static void main(String[] args) { //創(chuàng)建奶箱對象,這是共享數(shù)據(jù)區(qū)域 Box b = new Box(); //創(chuàng)建生產(chǎn)者對象,把奶箱對象作為構(gòu)造方法參數(shù)傳遞。因?yàn)樵谶@個(gè)類中要謂用存儲(chǔ)牛奶的操作 Producer p = new Producer(b); //創(chuàng)建消費(fèi)者對象,把奶箱對象作為構(gòu)造方法參數(shù)傳遞,因?yàn)樵谶@個(gè)類中要調(diào)用獲取牛奶的操作 Customer c =new Customer(b); //創(chuàng)建2個(gè)線程對象,分別把生產(chǎn)者對象和消費(fèi)者對象作為構(gòu)造方法參數(shù)傳遞 Thread t1 = new Thread(p); Thread t2 = new Thread(c); //啟動(dòng)線程 t1.start(); t2.start();// 送奶工將第1瓶奶放入奶箱// 用戶拿到第1瓶奶// 送奶工將第2瓶奶放入奶箱// 用戶拿到第2瓶奶// 送奶工將第3瓶奶放入奶箱// 用戶拿到第3瓶奶// 送奶工將第4瓶奶放入奶箱// 用戶拿到第4瓶奶// 送奶工將第5瓶奶放入奶箱// 用戶拿到第5瓶奶 }}
關(guān)于“Java如何實(shí)現(xiàn)多線程、線程同步”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“Java如何實(shí)現(xiàn)多線程、線程同步”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
名稱欄目:Java如何實(shí)現(xiàn)多線程、線程同步
分享網(wǎng)址:http://jinyejixie.com/article4/joggie.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供域名注冊、網(wǎng)站導(dǎo)航、網(wǎng)站維護(hù)、網(wǎng)站改版、網(wǎng)站設(shè)計(jì)、Google
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)