在java中可有兩種方式實現多線程,一種是繼承Thread類,一種是實現Runnable接口;Thread類是在java.lang包中定義的。一個類只要繼承了Thread類同時覆寫了本類中的run()方法就可以實現多線程操作了,但是一個類只能繼承一個父類,這是此方法的局限。
1 public class Actor extends Thread { 2 public void run(){ 3 //線程執行的操作 4 } 5 }
在實際開發中一個多線程的操作很少使用Thread類,而是通過Runnable接口完成。
1 public class Actress implements Runnable{ 2 3 @Override 4 public void run() { 5 //線程執行的操作 6 } 7 }
在主方法中調用這兩種線程。
1 public static void main(String[] args) { 2 Thread actor=new Actor(); 3 actor.setName("Mr.thread"); //Thread 線程 4 actor.start(); 5 6 Thread actressThread=new Thread(new Actress(),"Miss.Runnable"); //Runnable 線程 7 actressThread.start(); 8 }
兩種實現方式的區別和聯系:
在程序開發中只要是多線程肯定永遠以實現Runnable接口為主,因為實現Runnable接口相比繼承Thread類有如下好處:
1、避免點繼承的局限,一個類可以繼承多個接口。
2、適合於資源的共享
以賣票程序為例,通過Thread類完成:
1 package multithreading; 2 3 public class MyThreadWithExtends extends Thread { 4 5 private int tickets = 10; 6 7 @Override 8 public void run() { 9 10 for (int i = 0; i <= 100; i++) { 11 if(tickets>0){ 12 System.out.println(Thread.currentThread().getName()+"--賣出票:" + tickets--); 13 } 14 } 15 } 16 17 18 public static void main(String[] args) { 19 MyThreadWithExtends thread1 = new MyThreadWithExtends(); 20 MyThreadWithExtends thread2 = new MyThreadWithExtends(); 21 MyThreadWithExtends thread3 = new MyThreadWithExtends(); 22 23 thread1.start(); 24 thread2.start(); 25 thread3.start(); 26 27 //每個線程都獨立,不共享資源,每個線程都賣出了10張票,總共賣出了30張。如果真賣票,就有問題了。 28 } 29 30 }
運行結果:
Thread-0--賣出票:10
Thread-2--賣出票:10
Thread-1--賣出票:10
Thread-2--賣出票:9
Thread-0--賣出票:9
Thread-2--賣出票:8
Thread-1--賣出票:9
Thread-2--賣出票:7
Thread-0--賣出票:8
Thread-2--賣出票:6
Thread-2--賣出票:5
Thread-2--賣出票:4
Thread-1--賣出票:8
Thread-2--賣出票:3
Thread-0--賣出票:7
Thread-2--賣出票:2
Thread-2--賣出票:1
Thread-1--賣出票:7
Thread-0--賣出票:6
Thread-1--賣出票:6
Thread-0--賣出票:5
Thread-0--賣出票:4
Thread-1--賣出票:5
Thread-0--賣出票:3
Thread-1--賣出票:4
Thread-1--賣出票:3
Thread-1--賣出票:2
Thread-0--賣出票:2
Thread-1--賣出票:1
Thread-0--賣出票:1
如果用Runnable就可以實現資源共享,下面看例子:
1 package multithreading; 2 3 public class MyThreadWithImplements implements Runnable { 4 5 private int tickets = 10; 6 7 @Override 8 public void run() { 9 10 for (int i = 0; i <= 100; i++) { 11 if(tickets>0){ 12 System.out.println(Thread.currentThread().getName()+"--賣出票:" + tickets--); 13 } 14 } 15 } 16 17 18 public static void main(String[] args) { 19 MyThreadWithImplements myRunnable = new MyThreadWithImplements(); 20 Thread thread1 = new Thread(myRunnable, "窗口一"); 21 Thread thread2 = new Thread(myRunnable, "窗口二"); 22 Thread thread3 = new Thread(myRunnable, "窗口三"); 23 24 thread1.start(); 25 thread2.start(); 26 thread3.start(); 27 } 28 29 }
運行結果:
窗口二--賣出票:10
窗口三--賣出票:9
窗口一--賣出票:8
窗口三--賣出票:6
窗口三--賣出票:4
窗口三--賣出票:3
窗口三--賣出票:2
窗口三--賣出票:1
窗口二--賣出票:7
窗口一--賣出票:5
每個線程共享了對象myRunnable的資源,賣出的總票數是對的,但是順序是亂的,怎么辦?
同步關鍵詞synchronized
線程執行的時候,一個個執行不就有序了。即線程1在執行的時候,其他線程阻塞不要執行。
加synchronize。
1 package multithreading.sync; 2 3 public class MyThreadWithImplements implements Runnable { 4 5 private int tickets = 10; 6 7 @Override 8 public synchronized void run() { 9 //同步關鍵詞synchronized 10 for (int i = 0; i <= 100; i++) { 11 if(tickets>0){ 12 System.out.println(Thread.currentThread().getName()+"--賣出票:" + tickets--); 13 } 14 } 15 } 16 17 18 public static void main(String[] args) { 19 20 MyThreadWithImplements myRunnable = new MyThreadWithImplements(); 21 Thread thread1 = new Thread(myRunnable, "窗口一"); 22 Thread thread2 = new Thread(myRunnable, "窗口二"); 23 Thread thread3 = new Thread(myRunnable, "窗口三"); 24 25 thread1.start(); 26 thread2.start(); 27 thread3.start(); 28 } 29 30 }
運行結果
窗口一--賣出票:10
窗口一--賣出票:9
窗口一--賣出票:8
窗口一--賣出票:7
窗口一--賣出票:6
窗口一--賣出票:5
窗口一--賣出票:4
窗口一--賣出票:3
窗口一--賣出票:2
窗口一--賣出票:1
缺陷
1、如果這個獲取鎖的線程由於要等待IO或者其他原因(比如調用sleep方法)被阻塞了,但是又沒有釋放鎖,其他線程便只能干巴巴地等待,這多么影響程序執行效率。
2、當有多個線程讀寫文件時,讀寫操作會發生沖突現象,寫操作會發生沖突現象,但是讀操作不會發生沖突現象。但是采用synchronized關鍵字來實現同步的話,就會導致一個問題:如果多個線程都只是進行讀操作,當一個線程在進行讀操作時,其他線程只能等待無法進行讀操作。
因此就需要一種機制來使得多個線程都只是進行讀操作時,線程之間不會發生沖突,通過Lock就可以辦到。另外,通過Lock可以知道線程有沒有成功獲取到鎖。這個是synchronized無法辦到的。總的來說,也就是說Lock提供了比synchronized更多的功能。
ReadWriteLock讀寫文件
概述
ReadWriteLock是一個接口,在它里面只定義了兩個方法:一個讀的鎖和一個寫的鎖。
讀的鎖:A線程獲取了讀的鎖,那么B線程也可以獲取讀的鎖。
寫的鎖:A線程獲取了寫的鎖,那么B線程不能獲取讀也不能獲取寫的鎖。
1 public interface ReadWriteLock { 2 /** 3 * Returns the lock used for reading. 4 * 讀的鎖,A線程獲取了讀的鎖,那么B線程也可以獲取讀的鎖 5 * @return the lock used for reading. 6 */ 7 Lock readLock(); 8 9 /** 10 * Returns the lock used for writing. 11 * 寫的鎖,A線程獲取了寫的鎖,那么B線程不能獲取讀也不能獲取寫的鎖。 12 * @return the lock used for writing. 13 */ 14 Lock writeLock(); 15 }
。。。未完待續