Java面試大全(2020年版)271-280

271.啓動一個線程是用run()還是start()?

啓動一個線程是調用start()方法,使線程就緒狀態,以後可以被調度爲運行狀態,一個線程必須關聯一些具體的執行代碼,run()方法是該線程所關聯的執行代碼。

272.當一個線程進入一個對象的一個synchronized方法後,其它線程是否可進入此對象的其它方法?

分幾種情況:

  • 其他方法前是否加了synchronized關鍵字,如果沒加,則能。
  • 如果這個方法內部調用了wait,則可以進入其他synchronized方法。
  • 如果其他個方法都加了synchronized關鍵字,並且內部沒有調用wait,則不能。
  • 如果其他方法是static,它用的同步鎖是當前類的字節碼,與非靜態的方法不能同步,因爲非靜態的方法用的是this。

273.線程的基本概念、線程的基本狀態以及狀態之間的關係

一個程序中可以有多條執行線索同時執行,一個線程就是程序中的一條執行線索,每個線程上都關聯有要執行的代碼,即可以有多段程序代碼同時運行,每個程序至少都有一個線程,即main方法執行的那個線程。如果只是一個cpu,它怎麼能夠同時執行多段程序呢?這是從宏觀上來看的,cpu一會執行a線索,一會執行b線索,切換時間很快,給人的感覺是a,b在同時執行,好比大家在同一個辦公室上網,只有一條鏈接到外部網線,其實,這條網線一會爲a傳數據,一會爲b傳數據,由於切換時間很短暫,所以,大家感覺都在同時上網。
狀態:就緒,運行,synchronize阻塞,wait和sleep掛起,結束。wait必須在synchronized內部調用。
調用線程的start方法後線程進入就緒狀態,線程調度系統將就緒狀態的線程轉爲運行狀態,遇到synchronized語句時,由運行狀態轉爲阻塞,當synchronized獲得鎖後,由阻塞轉爲運行,在這種情況可以調用wait方法轉爲掛起狀態,當線程關聯的代碼執行完後,線程變爲結束狀態。

274.簡述synchronized和java.util.concurrent.locks.Lock的異同 ?

主要相同點:Lock能完成synchronized所實現的所有功能
主要不同點:Lock有比synchronized更精確的線程語義和更好的性能。synchronized會自動釋放鎖,而Lock一定要求程序員手工釋放,並且必須在finally從句中釋放。Lock還有更強大的功能,例如,它的tryLock方法可以非阻塞方式去拿鎖。
舉例說明(對下面的題用lock進行了改寫):

package com.huawei.interview;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadTest {

	/**
	 * @param args
	 */
	
	private int j;
	private Lock lock = new ReentrantLock();
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ThreadTest tt = new ThreadTest();
		for(int i=0;i<2;i++)
		{
			new Thread(tt.new Adder()).start();
			new Thread(tt.new Subtractor()).start();
		}
	}

	private class Subtractor implements Runnable
	{

		@Override
		public void run() {
			// TODO Auto-generated method stub
			while(true)
			{
				/*synchronized (ThreadTest.this) {			
					System.out.println("j--=" + j--);
					//這裏拋異常了,鎖能釋放嗎?
				}*/
				lock.lock();
				try
				{
					System.out.println("j--=" + j--);
				}finally
				{
					lock.unlock();
				}
			}
		}
		
	}
	
	private class Adder implements Runnable
	{

		@Override
		public void run() {
			// TODO Auto-generated method stub
			while(true)
			{
				/*synchronized (ThreadTest.this) {
				System.out.println("j++=" + j++);	
				}*/
				lock.lock();
				try
				{
					System.out.println("j++=" + j++);
				}finally
				{
					lock.unlock();
				}				
			}			
		}
		
	}
}

275.設計4個線程,其中兩個線程每次對j增加1,另外兩個線程對j每次減少1。寫出程序。

以下程序使用內部類實現線程,對j增減的時候沒有考慮順序問題。

public class ThreadTest1 
{ 
private int j; 
public static void main(String args[]){ 
   ThreadTest1 tt=new ThreadTest1(); 
   Inc inc=tt.new Inc(); 
   Dec dec=tt.new Dec(); 
   for(int i=0;i<2;i++){ 
       Thread t=new Thread(inc); 
       t.start(); 
		   t=new Thread(dec); 
       t.start(); 
       } 
   } 
private synchronized void inc(){ 
   j++; 
   System.out.println(Thread.currentThread().getName()+"-inc:"+j); 
   } 
private synchronized void dec(){ 
   j--; 
   System.out.println(Thread.currentThread().getName()+"-dec:"+j); 
   } 
class Inc implements Runnable{ 
   public void run(){ 
       for(int i=0;i<100;i++){ 
       inc(); 
       } 
   } 
} 
class Dec implements Runnable{ 
   public void run(){ 
       for(int i=0;i<100;i++){ 
       dec(); 
       } 
   } 
} 
} 

----------隨手再寫的一個-------------
class A
{
JManger j =new JManager();
main()
{
	new A().call();
}

void call
{
	for(int i=0;i<2;i++)
	{
		new Thread(
			new Runnable(){ public void run(){while(true){j.accumulate()}}}
		).start();
		new Thread(new Runnable(){ public void run(){while(true){j.sub()}}}).start();
	}
}
}

class JManager
{
	private j = 0;
	
	public synchronized void subtract()
	{
		j--
	}
	
	public synchronized void accumulate()
	{
		j++;
	}
	
}

276.子線程循環10次,接着主線程循環100,接着又回到子線程循環10次,接着再回到主線程又循環100,如此循環50次,請寫出程序。

最終的程序代碼如下:

public class ThreadTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new ThreadTest().init();

	}

	public void init()
	{
		final Business business = new Business();
		new Thread(
				new Runnable()
				{

					public void run() {
						for(int i=0;i<50;i++)
						{
							business.SubThread(i);
						}						
					}
					
				}
		
		).start();
		
		for(int i=0;i<50;i++)
		{
			business.MainThread(i);
		}		
	}
	
	private class Business
	{
		boolean bShouldSub = true;//這裏相當於定義了控制該誰執行的一個信號燈
		public synchronized void MainThread(int i)
		{
			if(bShouldSub)
				try {
					this.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}		
				
			for(int j=0;j<5;j++)
			{
				System.out.println(Thread.currentThread().getName() + ":i=" + i +",j=" + j);
			}
			bShouldSub = true;
			this.notify();
		
		}
		
		
		public synchronized void SubThread(int i)
		{
			if(!bShouldSub)
				try {
					this.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}	
				
			for(int j=0;j<10;j++)
			{
				System.out.println(Thread.currentThread().getName() + ":i=" + i +",j=" + j);
			}
			bShouldSub = false;				
			this.notify();			
		}
	}
}
備註:不可能一上來就寫出上面的完整代碼,最初寫出來的代碼如下,問題在於兩個線程的代碼要參照同一個變量,即這兩個線程的代碼要共享數據,所以,把這兩個線程的執行代碼搬到同一個類中去:

package com.huawei.interview.lym;

public class ThreadTest {
	
	private static boolean bShouldMain = false;
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		/*new Thread(){
		public void run()
		{
			for(int i=0;i<50;i++)
			{
				for(int j=0;j<10;j++)
				{
					System.out.println("i=" + i + ",j=" + j);
				}
			}				
		}
		
	}.start();*/		
		
		
		//final String str = new String("");

		new Thread(
				new Runnable()
				{
					public void run()
					{
						for(int i=0;i<50;i++)
						{
							synchronized (ThreadTest.class) {
								if(bShouldMain)
								{
									try {
										ThreadTest.class.wait();} 
									catch (InterruptedException e) {
										e.printStackTrace();
									}
								}
								for(int j=0;j<10;j++)
								{
									System.out.println(
											Thread.currentThread().getName() + 
											"i=" + i + ",j=" + j);
								}
								bShouldMain = true;
								ThreadTest.class.notify();
							}							
						}						
					}
				}
		).start();
		
		for(int i=0;i<50;i++)
		{
			synchronized (ThreadTest.class) {
				if(!bShouldMain)
				{
					try {
						ThreadTest.class.wait();} 
					catch (InterruptedException e) {
						e.printStackTrace();
					}
				}				
				for(int j=0;j<5;j++)
				{
					System.out.println(
							Thread.currentThread().getName() + 						
							"i=" + i + ",j=" + j);
				}
				bShouldMain = false;
				ThreadTest.class.notify();				
			}			
		}
	}

}
下面使用jdk5中的併發庫來實現的:
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;

public class ThreadTest
{
	private static Lock lock = new ReentrantLock();
	private static Condition subThreadCondition = lock.newCondition();
	private static boolean bBhouldSubThread = false;
	public static void main(String [] args)
	{
		ExecutorService threadPool = Executors.newFixedThreadPool(3);
		threadPool.execute(new Runnable(){
			public void run()
			{
				for(int i=0;i<50;i++)
				{
					lock.lock();					
					try
					{					
						if(!bBhouldSubThread)
							subThreadCondition.await();
						for(int j=0;j<10;j++)
						{
							System.out.println(Thread.currentThread().getName() + ",j=" + j);
						}
						bBhouldSubThread = false;
						subThreadCondition.signal();
					}catch(Exception e)
					{						
					}
					finally
					{
						lock.unlock();
					}
				}			
			}
			
		});
		threadPool.shutdown();
		for(int i=0;i<50;i++)
		{
				lock.lock();					
				try
				{	
					if(bBhouldSubThread)
							subThreadCondition.await();								
					for(int j=0;j<10;j++)
					{
						System.out.println(Thread.currentThread().getName() + ",j=" + j);
					}
					bBhouldSubThread = true;
					subThreadCondition.signal();					
				}catch(Exception e)
				{						
				}
				finally
				{
					lock.unlock();
				}					
		}
	}
}

277.介紹Collection框架的結構

天南海北誰便談

278.Collection框架中實現比較要實現什麼接口

comparable/comparator

279.ArrayList和Vector的區別

這兩個類都實現了List接口(List接口繼承了Collection接口),他們都是有序集合,即存儲在這兩個集合中的元素的位置都是有順序的,相當於一種動態的數組,我們以後可以按位置索引號取出某個元素,,並且其中的數據是允許重複的,這是HashSet之類的集合的最大不同處,HashSet之類的集合不可以按索引號去檢索其中的元素,也不允許有重複的元素(本來題目問的與hashset沒有任何關係,但爲了說清楚ArrayList與Vector的功能,我們使用對比方式,更有利於說明問題)。
接着才說ArrayList與Vector的區別,這主要包括兩個方面:

  • 同步性:Vector是線程安全的,也就是說是它的方法之間是線程同步的,而ArrayList是線程序不安全的,它的方法之間是線程不同步的。如果只有一個線程會訪問到集合,那最好是使用ArrayList,因爲它不考慮線程安全,效率會高些;如果有多個線程會訪問到集合,那最好是使用Vector,因爲不需要我們自己再去考慮和編寫線程安全的代碼。
    備註:對於Vector&ArrayList、Hashtable&HashMap,要記住線程安全的問題,記住Vector與Hashtable是舊的,是java一誕生就提供了的,它們是線程安全的,ArrayList與HashMap是java2時才提供的,它們是線程不安全的。所以,我們講課時先講老的。
  • 數據增長:ArrayList與Vector都有一個初始的容量大小,當存儲進它們裏面的元素的個數超過了容量時,就需要增加ArrayList與Vector的存儲空間,每次要增加存儲空間時,不是隻增加一個存儲單元,而是增加多個存儲單元,每次增加的存儲單元的個數在內存空間利用與程序效率之間要取得一定的平衡。Vector默認增長爲原來兩倍,而ArrayList的增長策略在文檔中沒有明確規定(從源代碼看到的是增長爲原來的1.5倍)。ArrayList與Vector都可以設置初始的空間大小,Vector還可以設置增長的空間大小,而ArrayList沒有提供設置增長空間的方法。
    總結:即Vector增長原來的一倍,ArrayList增加原來的0.5倍。

280.HashMap和Hashtable的區別

HashMap是Hashtable的輕量級實現(非線程安全的實現),他們都完成了Map接口,主要區別在於HashMap允許空(null)鍵值(key),由於非線程安全,在只有一個線程訪問的情況下,效率要高於Hashtable。
HashMap允許將null作爲一個entry的key或者value,而Hashtable不允許。
HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因爲contains方法容易讓人引起誤解。
Hashtable繼承自Dictionary類,而HashMap是Java1.2引進的Map interface的一個實現。
最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多個線程訪問Hashtable時,不需要自己爲它的方法實現同步,而HashMap 就必須爲之提供外同步。
Hashtable和HashMap採用的hash/rehash算法都大概一樣,所以性能不會有很大的差異。
就HashMap與HashTable主要從三方面來說。
一.歷史原因:Hashtable是基於陳舊的Dictionary類的,HashMap是Java 1.2引進的Map接口的一個實現
二.同步性:Hashtable是線程安全的,也就是說是同步的,而HashMap是線程序不安全的,不是同步的
三.值:只有HashMap可以讓你將空值作爲一個表的條目的key或value

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章