---
# System prepended metadata

title: 15 Threads and Locks
tags: [提升程式設計師的面試力]

---

###### tags: `提升程式設計師的面試力`
# 15 Threads and Locks 

在微軟、谷歌或亞馬遜的面試中，被要求用線程實現算法的情況並不常見（除非你在一個團隊工作，而這對你來說是一項特別重要的技能）。
然而，對於任何公司的面試官來說，評估你對線程，尤其是你對死鎖的理解。
本章將介紹該主題。

Java中的線程
Java 中的每個線程都是由 java.lang 的唯一對象創建和控制的。 線程類。 運行獨立應用程序時，會自動創建一個用戶線程來執行 main() 方法。 這個線程稱為主線程。
在 Java 中，我們可以通過以下兩種方式之一來實現線程：


Implementing the Runnable Interface 
```java=
public interface Runnable {
    void run();
} 
```
```java=
public class RunnableThreadExample implements Runnable {
    public int count = 0;
    
    public void run() {
    System.out.println("RunnableThread starting.");
    try {
        while (count< 5) {
            Thread.sleep(500);
            count++;
        }
    } catch (InterruptedException exc) {
        System.out.println("RunnableThread interrupted.");
    }
        System.out.println("RunnableThread terminating.");
    }
}

public static void main(String[] args) {
    RunnableThreadExample instance = new RunnableThreadExample();
    Thread thread= new Thread(instance);
    thread.start();
    /* waits until above thread counts to 5 (slowly) */
    while (instance.count != 5) {
        try {
            Thread.sleep(250);
        } catch (InterruptedException exc) {
            exc.printStackTrace();
        }
    }
}
```

* 在上面的代碼中，觀察到我們真正需要做的就是讓我們的類實現 run() 方法
（第 4 行）。 然後另一個方法可以將類的實例傳遞給 new Thread (obj)（第 19 - 20 行）和
在線程上調用 start() （第 21 行）。

* 擴展線程類
或者，我們可以通過擴展 Thread 類來創建線程。 這幾乎總是意味著
我們重寫了 run() 方法，子類也可以在其內部顯式調用線程構造函數
構造函數。

```java=
public class ThreadExample extends Thread {
    int count = 0;

    public void run() {
        System.out.println("Thread starting.");
        try {
            while (count < 5) {
                Thread.sleep(500);
                System.out.println("In Thread, count is " + count);
            }
            count++;
        } catch (InterruptedException exc) {
            System.out.println("Thread interrupted.");
        }
        System.out.println("Thread terminating.");
    }
}

public class fxampleB {
    public static void main(String args[]) {
     ThreadExample instance = new ThreadExample();
        instance.start();

        while (instance.count != 5) {
            try {
                Thread.sleep(250);
            } catch (InterruptedException exc) {
                exc.printStackTrace();
            }
        }
    }
}
```

### 此代碼與第一種方法非常相似。 不同之處在於，由於我們正在擴展 Thread
類，而不僅僅是實現一個接口，我們可以在類本身的實例上調用 start()。

### 擴展線程類vs實現可運行接口
* 創建線程時，實現 Runnable 接口可能比擴展 Thread 類更可取的原因有兩個：
1. Java 不支持多重繼承。 因此，擴展 Thread 類意味著子類不能擴展任何其他類。 實現 Runnable 接口的類將能夠擴展另一個類。
2. 一個類可能只對可運行性感興趣，因此，繼承 Thread 類的全部開銷會過多

### Multithreading in Java Explained in 10 Minutes
{%youtube r_MbozD32eo %}

## Synchronization and Locks

* 給定進程中的線程共享相同的內存空間，這既是積極的，也是消極的。 它使線程能夠共享數據，這可能很有價值。 然而，它也為問題創造了機會當兩個線程同時修改一個資源時。 Java提供同步以控制訪問共享資源。

### Synchronized Methods

```java=
public class MyClass extends Thread {
    private String name;
    private MyObject myObj;
    
    public MyClass(MyObject obj, String n) {
        name = n;
        myObj = obj;
    }
    
    public void run() {
        myObj.foo(name);
    }
}

public class MyObject {
    public synchronized void foo(String name) {
        try {
            System.out.println("Thread " + name + ".foo(): starting");
            Thread.sleep(3000);
            System.out.println("Thread " + name + ".foo(): ending");
        } catch (InterruptedException exc) {
            System.out.println("Thread " + name + ": interrupted.");
        }
    }
}
```


當然，我們已經添加了代碼來故意減慢提款和存款的執行速度，因為它
有助於說明可能發生的潛在問題。您可能不會完全像這樣編寫代碼，但是
它所反映的情況非常非常真實。使用鎖將有助於保護共享資源不被修改
意想不到的方式。
### 死鎖和死鎖預防
死鎖是一個線程正在等待另一個線程持有的對象鎖的情況，而這
第二個線程正在等待第一個線程持有的對象鎖（或具有多個
線程）。由於每個線程都在等待另一個線程放棄鎖，所以它們都在等待
永遠。線程被稱為死鎖。
為了發生死鎖，您必須滿足以下所有四個條件：

- 1.互斥：在給定時間只有一個進程可以訪問資源。 （或者，更準確地說，有對資源的訪問受限。如果資源數量有限，也可能發生死鎖。）
- 2. 持有和等待：已經持有資源的進程可以請求額外的資源，而不會放棄它們當前的資源。
- 3. 無搶占：一個進程不能強行移除另一個進程的資源。
- 4. 循環等待：兩個或多個進程形成一個循環鏈，其中每個進程都在等待另一個進程
鏈中的資源。



### Q1  Thread vs. Process: What's the difference between a thread and a process?
* 線程與進程：線程和進程有什麼區別？
### A1 
https://totoroliu.medium.com/program-process-thread-%E5%B7%AE%E7%95%B0-4a360c7345e5

進程Processes和線程Thread彼此相關，但本質上是不同的。

進程可以被認為是正在執行的程序的一個實例。
進程是分配系統資源（例如 CPU 時間和內存）的獨立實體。每個進程都在單獨的地址空間中執行，一個進程不能訪問另一個進程的變量和數據結構。如果一個進程希望訪問另一個進程的資源，則必須使用進程間通信。這些包括管道、文件、套接字和其他形式。

線程存在於進程中並共享進程的資源（包括其堆空間）。同一進程中的多個線程將共享相同的堆空間。這與進程有很大不同，進程不能直接訪問另一個進程的內存。每個線程仍然有自己的寄存器和自己的堆棧，但其他線程可以讀取和寫入堆內存。

線程是進程的特定執行路徑。當一個線程修改進程資源時，該更改立即對兄弟線程可見。

### Q2 Context Switch: How would you measure the time spent in a context switch? 
* 上下文切換：您如何衡量在上下文切換中花費的時間？
### A2 從缺

### Q3  Dining Philosophers:
* In the famous dining philosophers problem, a bunch of philosophers are sitting around a circular table with one chopstick between each of them. A philosopher needs both chopsticks to eat, and always picks up the left chopstick before the right one. A deadlock could potentially occur if all the philosophers reached for the left chopstick atthe same time. Using threads and locks, implement a simulation of the dining philosophers problem that prevents deadlocks. 
* 哲學家進餐：在著名的哲學家進餐問題中，一群哲學家是圍坐在一張圓桌旁，兩人中間夾著一根筷子。 哲學家需要兩者筷子要吃，總是先拿起左邊的筷子再拿起右邊的筷子。 僵局可能如果所有哲學家同時伸手去拿左筷子，就可能發生。 使用線程和鎖，實現防止死鎖的哲學家進餐問題的模擬。？
### A3 [從缺 危機就找維基](https://zh.wikipedia.org/zh-tw/%E5%93%B2%E5%AD%A6%E5%AE%B6%E5%B0%B1%E9%A4%90%E9%97%AE%E9%A2%98)

![](https://i.imgur.com/s8LBllo.png)


### Q4  Deadlock-Free Class: Design a class which provides a lock only if there are no possible deadlocks
* 無死鎖類：設計一個僅在沒有可能的死鎖時才提供鎖的類

* https://wangwilly.github.io/willywangkaa/2018/07/10/Operating-System-Deadlock/
* What is Deadlock & How to prevent Deadlock in Java? | Multithreading | Almighty Java
{%youtube 82IYrX0qdWs %}
 
### A4 從缺

### Q5  Call In Order: Suppose we have the following code:
```
public class Foo {
public Foo() { ... }
public void first() { ... }
public void second() { ... }
public void third() { ... }
}
```
* The same instance of Foo will be passed to three different threads. ThreadA will call first threadB will call second, and thread( will call third. Design a mechanism to ensure that first is called before second and second is called before third. 
* Foo 的同一個實例將被傳遞給三個不同的線程。 ThreadA 將調用第一個 threadB 將調用第二個，thread( 將調用第三個。設計一種機制以確保在第二個之前調用第一個，在第三個之前調用第二個。
### A5 從缺

### Q6  Synchronized Methods: 
* You are given a class with synchronized method A and a normal method B. If you have two threads in one instance of a program, can they both execute A at the same time? Can they execute A and B at the same time?
* 同步方法：給定一個具有同步方法 A 和普通方法 B 的類。如果一個程序的一個實例中有兩個線程，它們可以同時執行 A 嗎？ 他們可以同時執行A和B嗎？
### A6 從缺

### Q7  FizzBuzz: 
* In the classic problem FizzBuzz, you are told to print the numbers from 1 to n. However,when the number is divisible by 3, print "Fizz''. When it is divisible by 5, print "Buzz''. When it is divisible by 3 and 5, print"FizzBuzz''. In this problem, you are asked to do this in a multithreaded way.Implement a multithreaded version of FizzBuzz with four threads. One thread checks for divisibility of 3 and prints"Fizz''. Another thread is responsible for divisibility of 5 and prints"Buzz''. A third thread is responsible for divisibility of 3 and 5 and prints "FizzBuzz''. A fourth thread does the numbers. 
* 在經典問題 FizzBuzz 中，您被告知打印從 1 到 n 的數字。 但是，當數字可以被 3 整除時，打印“Fizz”。當它可以被 5 整除時，打印“Buzz”。 當它可以被 3 和 5 整除時，打印“FizzBuzz”。在這個問題中，您被要求以多線程方式執行此操作。使用四個線程實現 FizzBuzz 的多線程版本。一個線程檢查 3 的可除性並打印“ Fizz''。 另一個線程負責 5 的整除並打印“Buzz”。第三個線程負責 3 和 5 的整除並打印“FizzBuzz”。 第四個線程處理數字。
### A7 從缺

```php=
$n = 100;
for ($i=1; $i <= $n; $i++) { 
    if($i % 15 == 0){
        echo "FizzBuzz";
    }elseif($i % 3 == 0){
        echo "Fizz";
    }elseif($i % 5 == 0){
        echo "Buzz";
    }else{
        echo $i;
    }
}

```