前言
最近也面了好多家企業(yè)甩苛,也總結(jié)到很多筆試經(jīng)驗和面試經(jīng)驗。筆試大多數(shù)Java題目都是徘握荆客網(wǎng)原題和簡單排序讯蒲,數(shù)據(jù)庫,
Java
基礎(chǔ)概念肄扎,數(shù)據(jù)結(jié)構(gòu)墨林,MVC
模式等。面試官問的題目涉及的知識無非是Java
基礎(chǔ)知識反浓,設(shè)計模式萌丈,網(wǎng)絡(luò)等赞哗。我發(fā)現(xiàn)出現(xiàn)頻率很高的知識點有多線程雷则,設(shè)計模式(單例模式,策略模式肪笋,觀察者模式)等月劈。今天就來說一下筆試和面試中常見的多線程題目。
筆試
- 題目:有
A
藤乙,B
猜揪,C
三個線程,,A
線程輸出A
坛梁,B
線程輸出B
而姐,C
線程輸出C
,要求划咐,同時啟動三個線程,拴念,按順序輸出ABC
钧萍,循環(huán)10
次。這道題目出現(xiàn)的頻率很高啊政鼠。
第一種思路
創(chuàng)建
3
個線程輪流輸出风瘦,用lock
對象去同步線程的狀態(tài),用count
變量標識出哪個線程公般,MAX
變量用于邊界控制万搔,適時退出輪詢。(沒有用到wait()和notify()線程通信機制)手寫代碼
public class PrintABC {
public static void main(String[] args) {
final Lock lock = new ReentrantLock();
Thread a = new Thread(new PrintfABCThread("A", lock, 0));
Thread b = new Thread(new PrintfABCThread("B", lock, 1));
Thread c = new Thread(new PrintfABCThread("C", lock, 2));
a.start();
b.start();
c.start();
}
}
class PrintfABCThread implements Runnable {
private String name;
private Lock lock;
private Integer flag;
public static int count = 0;
public static final int MAX = 30;
public PrintfABCThread(String name, Lock lock, Integer flag) {
this.name = name;
this.lock = lock;
this.flag = flag;
}
@Override
public void run() {
while (true) {
lock.lock();
if (count >= MAX) {
lock.unlock();
return;
}
if (count % 3 == flag) {
System.out.println(name);
count++;
}
lock.unlock();
}
}
}
-
輸出結(jié)果
第二種思路
通過
Thread
類的join()
方法讓我們開啟的線程加入到主線程官帘,只有我們開啟的新線程結(jié)束后瞬雹,主線程才能繼續(xù)執(zhí)行。(不滿足題意刽虹,創(chuàng)建了30個線程挖炬,而且沒有同時開啟線程)手寫代碼
public class PrintfABC {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
Thread a = new Thread(new PrintThread("A"));
a.start();
a.join();
Thread b = new Thread(new PrintThread("B"));
b.start();
b.join();
Thread c = new Thread(new PrintThread("C"));
c.start();
c.join();
}
}
}
class PrintThread implements Runnable {
private String name;
public PrintThread(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(name);
}
}
-
輸出結(jié)果
第三種思路
- 定義一個MainLock繼承于ReentrantLock,里面維護著3個condition状婶,用于線程之間的通信意敛。
public class MainLock extends ReentrantLock {
private static final long serialVersionUID = 7103258623232795241L;
private int count = 0;
private final int max;
private final Condition a;
private final Condition b;
private final Condition c;
public MainLock(int max) {
this.max = max;
this.a = this.newCondition();
this.b = this.newCondition();
this.c = this.newCondition();
}
public boolean isEnd() {
if (count >= max) {
return true;
}
return false;
}
public void increase() {
count++;
}
public int getCount() {
return this.count;
}
public int getMax() {
return this.max;
}
public Condition getA() {
return this.a;
}
public Condition getB() {
return this.b;
}
public Condition getC() {
return this.c;
}
public boolean isA() {
return count % 3 == 0;
}
public boolean isB() {
return count % 3 == 1;
}
public boolean isC() {
return count % 3 == 2;
}
}
- 創(chuàng)建一個定長線程池,開啟3個線程膛虫,分別去處理輸出A草姻,B,C的請求稍刀。
public class Main {
public static void main(String[] args) {
MainLock lock = new MainLock(30);
ExecutorService pool = Executors.newFixedThreadPool(3);
pool.submit(new AThread(lock));
pool.submit(new BThread(lock));
pool.submit(new CThread(lock));
pool.shutdown();
}
}
class AThread implements Runnable {
private final MainLock lock;
public AThread(MainLock lock) {
this.lock = lock;
}
@Override
public void run() {
while (true) {
lock.lock();
try {
if (lock.isA()) {
if (lock.isEnd()) {
System.exit(1);
} else {
print();
}
lock.increase();
lock.getB().signal();
} else {
try {
lock.getA().await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
lock.unlock();
}
}
}
private void print() {
System.out.println("A ");
}
}
class BThread implements Runnable {
private final MainLock lock;
public BThread(MainLock lock) {
this.lock = lock;
}
@Override
public void run() {
while (true) {
lock.lock();
try {
if (lock.isB()) {
if (lock.isEnd()) {
System.exit(1);
} else {
print();
}
lock.increase();
lock.getC().signal();
} else {
try {
lock.getB().await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
lock.unlock();
}
}
}
private void print() {
System.out.println("B ");
}
}
class CThread implements Runnable {
private final MainLock lock;
public CThread(MainLock lock) {
this.lock = lock;
}
@Override
public void run() {
while (true) {
lock.lock();
try {
if (lock.isC()) {
if (lock.isEnd()) {
System.exit(1);
} else {
print();
}
lock.increase();
lock.getA().signal();
} else {
try {
lock.getC().await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
lock.unlock();
}
}
}
private void print() {
System.out.println("C ");
}
}
-
輸出結(jié)果
- 第二個題目: 用多線程去處理
"abc"
撩独,"def"
,“ghi”
這個三個字符串账月,讓它們以"adg"
综膀,"beh"
,“cfi
”這種形式輸出局齿。這個題目之前是紅星美凱龍技術(shù)部筆試卷的壓軸題剧劝,分值是20
分。
第一種思路
其實跟第一個題目的解決思路是差不多抓歼,唯一變的就是我們要獲取下標訪問字符串從而獲取字符讥此。我們可以通過count
變量來標識由哪一個線程輸出,通過count / 3
獲取下標谣妻。(還是沒有用到wait()和notify()機制)
public class DemoTwo {
public static void main(String[] args) {
final Lock lock = new ReentrantLock();
Thread a = new Thread(new PrintThread("abc", lock, 0));
Thread b = new Thread(new PrintThread("def", lock, 1));
Thread c = new Thread(new PrintThread("ghi", lock, 2));
a.start();
b.start();
c.start();
}
}
class PrintThread implements Runnable {
private String name;
private Lock lock;
private Integer flag;
public static int count = 0;
public static int MAX = 9;
public PrintThread(String name, Lock lock, Integer flag) {
this.name = name;
this.lock = lock;
this.flag = flag;
}
@Override
public void run() {
while (true) {
lock.lock();
if (count >= MAX) {
lock.unlock();
return;
}
if (count % 3 == flag) {
System.out.print(name.charAt(count / 3) + " ");
count++;
}
lock.unlock();
}
}
}
-
輸出結(jié)果萄喳。
第二種思路
和上面的思路是一樣的。(沒有同時開啟3個線程)
手寫代碼
public class DemoOne {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 3; i++) {
Thread a = new Thread(new MyThread("abc", i));
a.start();
a.join();
Thread b = new Thread(new MyThread("def", i));
b.start();
b.join();
Thread c = new Thread(new MyThread("ghi", i));
c.start();
c.join();
System.out.println("");
}
}
}
class MyThread implements Runnable {
private String str;
private int index;
public MyThread(String str, int index) {
this.str = str;
this.index = index;
}
@Override
public void run() {
System.out.print(String.valueOf(str.charAt(index)) + " ");
}
}
-
輸出結(jié)果蹋半。
第三種思路
public class Main3 {
public static void main(String args[]) {
MainLock lock = new MainLock(9);
ExecutorService pool = Executors.newFixedThreadPool(3);
pool.submit(new XThread(lock));
pool.submit(new YThread(lock));
pool.submit(new ZThread(lock));
pool.shutdown();
}
}
class XThread implements Runnable {
private final MainLock lock;
public XThread(MainLock lock) {
this.lock = lock;
}
@Override
public void run() {
while (true) {
lock.lock();
try {
if (lock.isA()) {
if (lock.isEnd()) {
System.exit(1);
} else {
print();
}
lock.increase();
lock.getB().signal();
} else {
try {
lock.getA().await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
lock.unlock();
}
}
}
private void print() {
System.out.print("abc".charAt(lock.getCount() / 3));
}
}
class YThread implements Runnable {
private final MainLock lock;
public YThread(MainLock lock) {
this.lock = lock;
}
@Override
public void run() {
while (true) {
lock.lock();
try {
if (lock.isB()) {
if (lock.isEnd()) {
System.exit(1);
} else {
print();
}
lock.increase();
lock.getC().signal();
} else {
try {
lock.getB().await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
lock.unlock();
}
}
}
private void print() {
System.out.print("def".charAt(lock.getCount() / 3));
}
}
class ZThread implements Runnable {
private final MainLock lock;
public ZThread(MainLock lock) {
this.lock = lock;
}
@Override
public void run() {
while (true) {
lock.lock();
try {
if (lock.isC()) {
if (lock.isEnd()) {
System.exit(1);
} else {
print();
}
lock.increase();
lock.getA().signal();
} else {
try {
lock.getC().await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
lock.unlock();
}
}
}
private void print() {
System.out.print("ghi".charAt(lock.getCount() / 3));
}
}
-
輸出結(jié)果
面試
昨天去掃唄面試他巨,面試官問我多線程的實現(xiàn)的二種方式和彼此之間的區(qū)別。這個也很簡單,百度也爛大街了染突。
-
采用
extends Thread
方式優(yōu)點:編程簡單匪傍,如果要訪問當前線程,無需使用
Thread.currentThread()
方法觉痛,可以直接用this
役衡,即可獲取當前線程。缺點:由于繼承了
Thread
,類無法再繼承其他的父類薪棒。使用方式:直接
new
相應(yīng)的線程類即可手蝎。
-
采用
implements Runnable
方式優(yōu)點:沒有繼承
Thread
類,所以可以繼承其他的父類俐芯,在這種形式下棵介,多個線程可以共享同一個對象,所以非常合適多個相同的線程來處理同一份資源的情況下吧史,把cpu
代碼和數(shù)據(jù)分開邮辽,形成清晰的模型,較好的體現(xiàn)了面向?qū)ο蟮乃枷朊秤_m用場景吨述,比如賣票。缺點:編程稍微復(fù)雜钞脂,如果要訪問當前線程揣云,必須使用
Thread.currentThread()
方法。使用方式:不能直接創(chuàng)建所需類的對象并運行它冰啃,而是必須從
Thread
類的一個實例內(nèi)部啟動它邓夕。
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
尾言
就算失望不能絕望,明天又去面試阎毅,美滋滋焚刚。