(1)Java多線程有兩種實現方式:繼承Thread類和實現Runnable接口,Thread就是實現了Runnable接口。
兩個最簡單的線程例子:
- package chc.runnable;
- public class ThreadTest2 {
- public static void main(String[] args) throws InterruptedException {
- Thread1 t = new Thread1();
- //t.run(); //這裏也不能直接調用方法
- t.start();
- for (int i = 0; i < 100; i++) {
- System.out.println("main:"+i);
- }
- }
- }
- class Thread1 extends Thread{
- public void run() {
- for (int i = 0; i < 100; i++) {
- System.out.println("Thread-----:"+i);
- }
- }
- }
- package chc.runnable;
- public class ThreadTest1 {
- public static void main(String[] args) {
- Runnable1 r =new Runnable1();
- Thread t1=new Thread(r);
- t1.start();
- }
- }
- class Runnable1 implements Runnable {
- public void run() {
- // TODO Auto-generated method stub
- for (int i = 1; i <= 5; i++) {
- System.out.println("實現Runnable接口的線程----->"+i);
- }
- }
- }
通過上兩個例子發現,當啓動線程的時候並不影響主程序的繼續執行。
(2)線程同步問題
使用synchronnized關鍵字
銀行賬戶存取錢的問題:
- public class ThreadTest {
- public static void main(String[] args){
- ThreadA t1=new ThreadA();
- t1.start();
- ThreadB t2=new ThreadB();
- t2.start();
- }
- }
- class ThreadA extends Thread{
- @Override
- public void run(){
- for(int i=0;i<100;i++){
- account.add();
- try {
- sleep(10);//模擬銀行系統處理時間
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
- class ThreadB extends Thread{
- @Override
- public void run() {
- for(int i=0;i<100;i++){
- account.remove();
- try {
- sleep(10);<span style="font-family: SimSun;">//模擬銀行系統處理時間</span>
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
- class account {
- public static int count=1000;
- //減去100元
- public static synchronized void remove(){
- count=count-100;
- System.out.println("減去100元,卡內餘額"+count);
- }
- //增加100元
- public static synchronized void add(){
- count=count+100;
- System.out.println("加上100元,卡內餘額"+count);
- }
- }
因爲synchronized同步只對同一個對象中的方法有效,也就是說一個線程正在執行account的add()方法,另一個線程是可以調用到另一個對象的add方法的。
2、啓動一個線程是用run()還是start(),調用的時候有什麼區別?
當然是start()了,當調用線程的start()方法的時候,線程就會進入到就緒狀態
run()方法是線程的執行入口,當線程從就緒狀態進入到執行狀態時首先要從run()方法開始執行。
當然,我們也是可以直接通過線程對象調用該對象的run()方法的,只是這只是一次普通的調用,並沒有啓動任何一個線程。
當我們調用start()方法時,是另外啓動了一個線程去執行線程類的代碼,並不影響主程序的執行,但是調用run()方法的時候要等待run()方法內的代碼執
行完主程序才可以向下執行,舉個例子:
- public class ThreadDemo2 {
- public static void main(String[] args) {
- Thread5 t1=new Thread5();
- Thread6 t2=new Thread6();
- t1.run();
- t2.start();
- for(int i=0;i<100;i++){
- System.out.println("主進程執行:"+i);
- }
- }
- }
- class Thread5 extends Thread{
- @Override
- public void run() {
- for(int i=0;i<100;i++){
- System.out.println("Thread5執行:"+i);
- }
- }
- }
- class Thread6 extends Thread{
- @Override
- public void run() {
- for(int i=0;i<100;i++){
- System.out.println("Thread6執行:"+i);
- }
- }
- }
3、當一個線程進入到一個對象的synchronized方法,那麼其他線程是否可以進入該對象的其它方法?
不一定,看情況
如果其它方法加了static關鍵字,那麼該方法屬於類,不屬於對象,不能與對象的方法保持同步(即使有synchronized關鍵字),是能進入的。
如果其它方法不帶有static關鍵字且帶有synchronized關鍵字,那麼不能進入,如果不帶,則能。
再其次就看方法內部有沒有wait()方法釋放鎖了
4、子線程循環2次,接着主線程循環3次,接着子線程循環3次,接着主線程循環3次,如此循環5次,請寫出程序代碼。
- public class ThreadDemo5 {
- static boolean thread_flag=false;//指示子線程循環是否結束
- static boolean main_flag=true; //調用子線程的start方法後變爲true,循環等待thread_flag爲true(也就是子線程循環完)的時刻,主線程循環完又變爲false;
- public static void main(String[] args) {
- for(int k=0;k<5;k++){
- Thread7 t=new Thread7();
- t.start();
- main_flag=true;
- while (main_flag) {//循環等待thread_f
- if(thread_flag){
- for(int i=0;i<3;i++){
- System.out.println("主線程第一次循環"+(i+1)+"次");
- }
- thread_flag=false;
- main_flag=false;
- }
- }
- }
- }
- }
- class Thread7 extends Thread {
- static boolean flag=true;//標誌子線程第幾次循環,當值爲true的時候子線程循環2次,否則循環3次
- public void run() {
- if(flag){
- for(int i=0;i<2;i++){
- System.out.println("子線程第一次循環"+(i+1)+"次");
- }
- flag=false;
- }else{
- for(int i=0;i<3;i++){
- System.out.println("子線程第二次循環"+(i+1)+"次");
- }
- flag=true;
- }
- ThreadDemo5.thread_flag=true;
- }
- }
這個題就是要注意調用子線程的start方法的時候並不能阻止主程序繼續向下執行,所以我們要用變量來標記。
5、sleep()和wait()有何異同?
(1)首先一個最明顯的區別是 wait是Object類的方法,而sleep()是Thread類的靜態方法,誰調用了該方法誰去休眠,即使在a線程裏調用了b線程的sleep方法,實際上還是a線程去休眠.
(2)比較重要的一點是sleep沒有釋放出鎖,而wait釋放了鎖,是其他線程可以使用同步塊資源。
sleep不出讓系統資源;wait是進入線程等待池等待,出讓系統資源,其他線程可以佔用CPU。一般wait不會加時間限制,因爲如果wait線程的運行資源不夠,再出來也沒用,要
等待其他線程調用notify/notifyAll喚醒等待池中的所有線程,纔會進入就緒隊列等待OS分配系統資源。sleep(milliseconds)可以用時間指定使它自動喚醒過來,如果時間不到
只能調用interrupt()強行打斷。
(3)使用範圍:
wait,notify和notifyAll只能在同步控制方法或者同步控制塊裏面使用,而sleep可以在任何地方使用
synchronized(x){
x.notify()
//或者wait()
}
(4)sleep需要捕獲異常,而wait不需要。
6、現在有T1 T2 T3三個線程,怎樣保證T2在T1執行完之後執行 T3在T2執行完之後執行
這題主要是考察對join()方法的使用。
當線程A當中執行了線程B.join(),那麼A線程要等待B線程執行完纔可以執行。
- public class JoinDemo {
- public static void main(String[] args) {
- T1 t1=new T1("T1");
- T2 t2=new T2("T2");
- T3 t3=new T3("T3");
- t1.start();
- try {
- t1.join();
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- t2.start();
- try {
- t2.join();
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- t3.start();
- }
- }
- class T1 extends Thread{
- private String name;
- public T1(String name) {
- this.name=name;
- }
- @Override
- public void run() {
- for(int i=0;i<5;i++){
- try {
- sleep(5);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- System.out.println(this.name+"循環"+i);
- }
- }
- }
- class T2 extends Thread{
- private String name;
- public T2(String name) {
- this.name=name;
- }
- @Override
- public void run() {
- for(int i=0;i<5;i++){
- try {
- sleep(5);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- System.out.println(this.name+"循環"+i);
- }
- }
- }
- class T3 extends Thread{
- private String name;
- public T3(String name) {
- this.name=name;
- }
- @Override
- public void run() {
- for(int i=0;i<5;i++){
- System.out.println(this.name+"循環"+i);
- }
- }
- }
7、簡述synchronized和java.util.concurrent.locks.Lock的異同?
主要相同點:Lock能完成synchronized所實現的所有功能
主要不同點:Lock有比synchronized更精確的線程語義和更好的性能。synchronized會自動釋放鎖,而Lock一定要求程序員手工釋放,並且必須在finally從句中釋放。
Lock還有更強大的功能,例如,它的tryLock方法可以非阻塞方式去拿鎖。