java多线程系列(1)
1,為什么需要線程?
作用:提升cpu的利用率,如,早期的dos系統(tǒng),執(zhí)行2個(gè)命令時(shí)( command 1, command 2 ),如果command1【假如是磁盤(pán)遍歷文件的IO操作】執(zhí)行的時(shí)間比較長(zhǎng),那么command 2必須等待,這種方式就是同步阻塞,
cpu就閑置了,為了提高cpu的利用率,我們就要使用多線程,如果一個(gè)任務(wù)時(shí)間比較長(zhǎng),cpu就暫時(shí)掛起他,去執(zhí)行另外的線程,所以線程一般是異步的。
2,每一個(gè)進(jìn)程至少會(huì)有一個(gè)線程在運(yùn)行
public class Test {public static void main(String[] args) {//打印線程的名稱(chēng) System.out.println( Thread.currentThread().getName() );}}輸出結(jié)果為 "main" ,注意這個(gè)main是線程的名字,跟main函數(shù)的名字相同而已。
3,在java中實(shí)現(xiàn)多線程有2種方式
>繼承Thread類(lèi)
>實(shí)現(xiàn)Runnable接口
在run方法中寫(xiě)線程要執(zhí)行的任務(wù)
class MyThread extends Thread{public void run(){System.out.println( "MyThread::run" );} }public class ThreadUse1 {public static void main(String[] args) {MyThread mt = new MyThread();mt.start();System.out.println( "運(yùn)行結(jié)束" );}}?
從運(yùn)行結(jié)果可知,run方法是在之后執(zhí)行的,雖然start開(kāi)啟線程比? 【System.out.println( "運(yùn)行結(jié)束" );】 他早,這說(shuō)明,CPU在調(diào)用線程的時(shí)候,是隨機(jī)的
4,再次驗(yàn)證cpu調(diào)用線程的隨機(jī)性
class MyThreadRand extends Thread{public void run(){try{for ( int i = 0; i < 10; i++ ) {int time = ( int )( Math.random() * 1000 );Thread.sleep( time );System.out.println( "MyThread:" + Thread.currentThread().getName() );}}catch( InterruptedException e ){e.printStackTrace();}} }public class RandThread {public static void main(String[] args) {try{MyThreadRand mt = new MyThreadRand();mt.setName( "自定義線程" );mt.start();for ( int i = 0; i < 10; i++ ) {int time = ( int )( Math.random() * 1000 );Thread.sleep( time );System.out.println( "MainThread:" + Thread.currentThread().getName() );}}catch( InterruptedException e ){e.printStackTrace();}}}從執(zhí)行結(jié)果可知,線程的調(diào)度沒(méi)有什么規(guī)律,是隨機(jī)的, 這里補(bǔ)充一點(diǎn),start方法作用是通知 “線程規(guī)劃器”,這個(gè)線程已經(jīng)準(zhǔn)備好了,等待調(diào)用線程的run方法,就是讓系統(tǒng)安排一個(gè)時(shí)間來(lái)調(diào)用run方法。如果直接調(diào)用run方法,線程就變成同步方式了,必須等待MyThreadRand的run方法執(zhí)行完成之后,才會(huì)執(zhí)行main函數(shù)中的線程
5,start方法的順序,不代表線程的啟動(dòng)順序
class MyThreadStart extends Thread{private int i;public MyThreadStart( int i ) {this.i = i;}public void run(){System.out.println( i );} }public class RandThread2 {public static void main(String[] args) {MyThreadStart s1 = new MyThreadStart( 1 );MyThreadStart s2 = new MyThreadStart( 2 );MyThreadStart s3 = new MyThreadStart( 3 );MyThreadStart s4 = new MyThreadStart( 4 );MyThreadStart s5 = new MyThreadStart( 5 );MyThreadStart s6 = new MyThreadStart( 6 );MyThreadStart s7 = new MyThreadStart( 7 );MyThreadStart s8 = new MyThreadStart( 8 );MyThreadStart s9 = new MyThreadStart( 9 );MyThreadStart s10 = new MyThreadStart( 10 );s1.start();s2.start();s3.start();s4.start();s5.start();s6.start();s7.start();s8.start();s9.start();s10.start();}}6,實(shí)現(xiàn)Runnable接口
class MyThreadRunnable implements Runnable {public void run(){System.out.println( Thread.currentThread().getName() );} }public class ThreadRunnable {public static void main(String[] args) {MyThreadRunnable mt = new MyThreadRunnable();Thread t = new Thread( mt );t.setName( "自定義線程1" );t.start();}}?
那么兩種多線程的實(shí)現(xiàn)方式,有什么不同呢?
>繼承Thread類(lèi)
>實(shí)現(xiàn)Runnable接口
1,使用繼承Thread類(lèi)的方式,多線程之間的數(shù)據(jù)不共享
class MyThreadShare extends Thread{private int count = 5;public MyThreadShare( String name ){this.setName( name );}public void run(){while( count-- > 0 ){System.out.println( Thread.currentThread().getName() + "->" + count );}} }public class ThreadShare {public static void main(String[] args) { MyThreadShare mt1 = new MyThreadShare( "A" );MyThreadShare mt2 = new MyThreadShare( "B" );MyThreadShare mt3 = new MyThreadShare( "C" );mt1.start();mt2.start();mt3.start();} }2,而要想實(shí)現(xiàn)線程之間的數(shù)據(jù)共享,我們可以改一下
備注:線程數(shù)據(jù)共享與不共享,都有對(duì)應(yīng)的場(chǎng)景,比如火車(chē)站4個(gè)窗口賣(mài)票,很顯然需要線程共享數(shù)據(jù)。如:總共用10張票,如果窗口賣(mài)了1張,其他窗口就指剩下9張,這才是比較貼近實(shí)際的,如果用第一種方式,相當(dāng)于有40張余票了。
class MyThreadShare2 extends Thread{private int count = 5; public void run(){while( count-- > 0 ){System.out.println( Thread.currentThread().getName() + "->" + count );}} }public class ThreadShare2 {public static void main(String[] args) { MyThreadShare2 mt = new MyThreadShare2();Thread ta = new Thread( mt, "A" );Thread tb = new Thread( mt, "B" );Thread tc = new Thread( mt, "C" );ta.start();tb.start();tc.start();} }從結(jié)果上看,好像實(shí)現(xiàn)了,數(shù)據(jù)共享,但是有點(diǎn)異常,B->3 很明顯不對(duì),這種現(xiàn)象,在多線程編程里面,叫“線程非安全”。現(xiàn)實(shí)生活中也有類(lèi)似場(chǎng)景,比如4S店賣(mài)車(chē),兩個(gè)客戶同時(shí)預(yù)訂了這輛車(chē)。那估計(jì)少不了一番辯論。怎么解決這個(gè)問(wèn)題呢?一般來(lái)說(shuō),在客戶訂車(chē)之前,銷(xiāo)售員要先查看庫(kù)存,如果客戶下單,要把庫(kù)存占用。表明有人預(yù)訂,其他銷(xiāo)售員看見(jiàn)了,就知道車(chē)被預(yù)訂了。程序中也是類(lèi)似。如果要訪問(wèn)這個(gè)變量,我們就給他加鎖,類(lèi)似于銷(xiāo)售員占用庫(kù)存。在方法前加上synchronized關(guān)鍵字。那么其他線程訪問(wèn)的時(shí)候,必須拿到這把鎖,才能訪問(wèn)。synchronized可以在任意對(duì)象或者方法上加鎖。
class MyThreadShare2 extends Thread{private int count = 5; // public void run(){ //產(chǎn)生線程非安全問(wèn)題synchronized public void run(){while( count-- > 0 ){System.out.println( Thread.currentThread().getName() + "->" + count );}} }public class ThreadShare2 {public static void main(String[] args) { MyThreadShare2 mt = new MyThreadShare2();Thread ta = new Thread( mt, "A" );Thread tb = new Thread( mt, "B" );Thread tc = new Thread( mt, "C" );ta.start();tb.start();tc.start();} }?3,模擬用戶登錄場(chǎng)景,如果有兩個(gè)用戶登錄,我們讓其中一個(gè)用戶線程占時(shí)掛起。看下會(huì)出現(xiàn)什么情況
class Login {private static String userName;private static String userPwd;public static void doPost( String _userName, String _userPwd ){try {userName = _userName;if( userName.equals( "ghostwu" ) ) {Thread.sleep( 3000 ); }userPwd = _userPwd;System.out.println( userName + "---->" + userPwd );}catch( InterruptedException e ){e.printStackTrace();}} }class ThreadA extends Thread{public void run(){Login.doPost( "ghostwu", "abc123" );} }class ThreadB extends Thread{public void run(){Login.doPost( "ghostwuB", "abc1234" );} }public class UserLogin {public static void main(String[] args) {ThreadA ta = new ThreadA();ThreadB tb = new ThreadB();ta.start();tb.start();}}在A線程掛起的時(shí)候,他之前的賦值已經(jīng)被B線程改變了,所以結(jié)果與預(yù)想的ghostwu? abc123不同。很明顯,我們要上鎖。
synchronized public static void doPost( String _userName, String _userPwd ){try {userName = _userName;if( userName.equals( "ghostwu" ) ) {Thread.sleep( 3000 ); }userPwd = _userPwd;System.out.println( userName + "---->" + userPwd );}catch( InterruptedException e ){e.printStackTrace();}}?
總結(jié)
以上是生活随笔為你收集整理的java多线程系列(1)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: HTTPS 与 HTTP 的区别在哪?
- 下一篇: 【代码设计】C# 实现 AOP 面向切面