###前言面試
最近也面了好多家企業,也總結到不少筆試經驗和麪試經驗。筆試大多數Java題目都是牛客網原題和簡單排序,數據庫,
Java
基礎概念,數據結構,MVC
模式等。面試官問的題目涉及的知識無非是Java
基礎知識,設計模式,網絡等。我發現出現頻率很高的知識點有多線程,設計模式(單例模式,策略模式,觀察者模式)等。今天就來講一下筆試和麪試中常見的多線程題目。數據庫
###筆試編程
A
,B
,C
三個線程,,A
線程輸出A
,B
線程輸出B
, C
線程輸出C
,要求,同時啓動三個線程,,按順序輸出ABC
,循環10
次。這道題目出現的頻率很高啊。#####第一種思路設計模式
建立3
個線程輪流輸出,用lock
對象去同步線程的狀態,用count
變量標識出哪一個線程,MAX
變量用於邊界控制,適時退出輪詢。(沒有用到wait()和notify()線程通訊機制)bash
手寫代碼網絡
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();
}
}
}
複製代碼
#####第二種思路數據結構
經過Thread
類的join()
方法讓咱們開啓的線程加入到主線程,只有咱們開啓的新線程結束後,主線程才能繼續執行。(不知足題意,建立了30個線程,並且沒有同時開啓線程)多線程
手寫代碼ide
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);
}
}
複製代碼
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;
}
}
複製代碼
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 ");
}
}
複製代碼
"abc"
,"def"
,「ghi」
這個三個字符串,讓它們以"adg"
,"beh"
,「cfi
」這種形式輸出。這個題目以前是紅星美凱龍技術部筆試卷的壓軸題,分值是20
分。#####第一種思路 其實跟第一個題目的解決思路是差很少,惟一變的就是咱們要獲取下標訪問字符串從而獲取字符。咱們能夠經過count
變量來標識由哪個線程輸出,經過count / 3
獲取下標。(仍是沒有用到wait()和notify()機制)ui
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();
}
}
}
複製代碼
#####第二種思路
和上面的思路是同樣的。(沒有同時開啓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)) + " ");
}
}
複製代碼
#####第三種思路
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));
}
}
複製代碼
###面試 昨天去掃唄面試,面試官問我多線程的實現的二種方式和彼此之間的區別。這個也很簡單,百度也爛大街了。
採用extends Thread
方式
優勢:編程簡單,若是要訪問當前線程,無需使用Thread.currentThread()
方法,能夠直接用this
,便可獲取當前線程。
缺點:因爲繼承了Thread
,類沒法再繼承其餘的父類。
使用方式:直接new
相應的線程類便可。
採用implements Runnable
方式
優勢:沒有繼承Thread
類,因此能夠繼承其餘的父類,在這種形式下,多個線程能夠共享同一個對象,因此很是合適多個相同的線程來處理同一份資源的狀況下,把cpu
代碼和數據分開,造成清晰的模型,較好的體現了面向對象的思想。適用場景,好比賣票。
缺點:編程稍微複雜,若是要訪問當前線程,必須使用Thread.currentThread()
方法。
使用方式:不能直接建立所需類的對象並運行它,而是必須從Thread
類的一個實例內部啓動它。
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
複製代碼
###尾言
就算失望不能絕望,明天又去面試,美滋滋。