leetcode 多线程
1279. 红绿灯路口 easy
对共享资源的操作 加锁
解法: Lock
java
public class TrafficLight {
private final Lock lock = new ReentrantLock();
private volatile int road = 1;
public void carArrived(
int carId, // ID of the car
int roadId, // ID of the road the car travels on. Can be 1 (road A) or 2 (road B)
int direction, // Direction of the car
Runnable turnGreen, // Use turnGreen.run() to turn light to green on current road
Runnable crossCar // Use crossCar.run() to make car cross the intersection
) {
lock.lock();
if (road != roadId) {
turnGreen.run();
road = roadId;
}
crossCar.run();
lock.unlock();
}
}
解法: CAS
java
public class TrafficLight {
private final AtomicInteger atomic = new AtomicInteger();
private volatile int road = 1;
public void carArrived(
int carId, // ID of the car
int roadId, // ID of the road the car travels on. Can be 1 (road A) or 2 (road B)
int direction, // Direction of the car
Runnable turnGreen, // Use turnGreen.run() to turn light to green on current road
Runnable crossCar // Use crossCar.run() to make car cross the intersection
) {
try {
while (!atomic.compareAndSet(0, 1)) {
Thread.sleep(1);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (road != roadId) {
turnGreen.run();
road = roadId;
}
crossCar.run();
try {
while (!atomic.compareAndSet(1, 0)) {
Thread.sleep(1);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
解法: Synchronized
java
public class TrafficLight {
private final Object LOCK = new Object();
private volatile int road = 1;
public void carArrived(
int carId, // ID of the car
int roadId, // ID of the road the car travels on. Can be 1 (road A) or 2 (road B)
int direction, // Direction of the car
Runnable turnGreen, // Use turnGreen.run() to turn light to green on current road
Runnable crossCar // Use crossCar.run() to make car cross the intersection
) {
synchronized (LOCK) {
if (road != roadId) {
turnGreen.run();
road = roadId;
}
crossCar.run();
}
}
}
解法: Semaphore
java
public class TrafficLight {
Semaphore semaphore = new Semaphore(1);
private volatile int road = 1;
public void carArrived(
int carId, // ID of the car
int roadId, // ID of the road the car travels on. Can be 1 (road A) or 2 (road B)
int direction, // Direction of the car
Runnable turnGreen, // Use turnGreen.run() to turn light to green on current road
Runnable crossCar // Use crossCar.run() to make car cross the intersection
) {
try {
semaphore.acquire();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (road != roadId) {
turnGreen.run();
road = roadId;
}
crossCar.run();
semaphore.release();
}
}
1188. 设计有限阻塞队列 mid
实现一个拥有如下方法的线程安全有限阻塞队列:
- BoundedBlockingQueue(int capacity)构造方法初始化队列,其中capacity代表队列长度上限。
- void enqueue(int element)在队首增加一个element. 如果队列满,调用线程被阻塞直到队列非满。
- int dequeue()返回队尾元素并从队列中将其删除. 如果队列为空,调用线程被阻塞直到队列非空。
- int size()返回当前队列元素个数。
- 你的实现将会被多线程同时访问进行测试。每一个线程要么是一个只调用enqueue方法的生产者线程,要么是一个只调用dequeue方法的消费者线程。size方法将会在每一个测试用例之后进行调用。
请不要使用内置的有限阻塞队列实现,否则面试将不会通过。
Condition 控制锁
java
public class BoundedBlockingQueue {
private final AtomicInteger size = new AtomicInteger(0);
private final int capacity;
private final Queue<Integer> container = new LinkedList<>();
private final ReentrantLock lock = new ReentrantLock();
//用来通知生产(入队)线程等待await还是可以执行signal
private final Condition producer = lock.newCondition();
//用来通知消费(出队)线程等待await还是可以执行signal
private final Condition consumer = lock.newCondition();
public BoundedBlockingQueue(int capacity) {
this.capacity = capacity;
}
public void enqueue(int element) throws InterruptedException {
lock.lock();
try {
while (size.get() >= capacity) {
producer.await();
}
container.add(element);
size.incrementAndGet();
consumer.signal();
} finally {
lock.unlock();
}
}
public int dequeue() throws InterruptedException {
lock.lock();
try {
while (size.get() == 0) {
consumer.await();
}
int lastValue = container.remove();
size.decrementAndGet();
producer.signal();
return lastValue;
} finally {
lock.unlock();
}
}
public int size() {
lock.lock();
try {
return size.get();
} finally {
lock.unlock();
}
}
}
1242. 多线程网页爬虫 mid
题目描述叨逼叨, 实际就是让多线程请求资源
注意下 thread.join() 的使用方式
java
public class Solution {
Set<String> visited = new HashSet<>();
String rootHostname;
HtmlParser htmlParser;
class Task extends Thread {
String url;
public Task(String url) {
this.url = url;
}
@Override
public void run() {
List<String> subUrls = htmlParser.getUrls(url);
// 持有线程的引用
List<Thread> subTasks = new ArrayList<>();
for (String s : subUrls) {
if (visited.contains(s)
// 必须域名一致的路径
|| !rootHostname.equals(getHostName(s))) continue;
addUrl(visited, s);
Thread task = new Task(s);
subTasks.add(task);
task.start();
}
for (Thread task : subTasks) {
try {
task.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public List<String> crawl(String startUrl, HtmlParser htmlParser) {
this.htmlParser = htmlParser;
this.rootHostname = getHostName(startUrl);
addUrl(visited, startUrl);
Thread thread = new Task(startUrl);
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
return new ArrayList<>(visited);
}
static String getHostName(String url) {
final int start = 7;
int end = url.indexOf('/', start);
if (end == -1) {
end = url.length();
}
return url.substring(start, end);
}
static synchronized void addUrl(Set<String> result, String url) {
result.add(url);
}
}
1115. 交替打印 FooBar
两个不同的线程将会共用一个 FooBar 实例:
- 线程 A 将会调用 foo()方法,而
- 线程 B 将会调用 bar()方法
请设计修改程序,以确保 "foobar" 被输出 n 次。
java
class FooBar {
private int n;
private boolean flag = true;
private ReentrantLock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
public FooBar(int n) {
this.n = n;
}
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
try {
lock.lock();
while (!flag) condition1.await();
printFoo.run();
flag = false;
condition2.signal();
} finally {
lock.unlock();
}
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
try {
lock.lock();
while (flag) condition2.await();
printBar.run();
condition1.signal();
} finally {
lock.unlock();
}
}
}
}
三个线程 循环打印 123
java
class Solution {
static int cnt = 1;
static ReentrantLock lock = new ReentrantLock();
static Condition condition1;
static Condition condition2;
static Condition condition3;
public static void main(String[] args) {
condition1 = lock.newCondition();
condition2 = lock.newCondition();
condition3 = lock.newCondition();
Worker worker1 = new Worker(1, condition1, condition2);
Worker worker2 = new Worker(2, condition2, condition3);
Worker worker3 = new Worker(0, condition3, condition1);
worker1.start();
worker2.start();
worker3.start();
}
static class Worker extends Thread {
public Worker(int idx, Condition condition, Condition nextCondition) {
this.idx = idx;
this.condition = condition;
this.nextCondition = nextCondition;
}
int idx;
Condition condition;
Condition nextCondition;
@Override
public void run() {
Worker worker = (Worker) Thread.currentThread();
while (true) {
// 非公平锁会在这里抢锁
lock.lock();
try {
// 释放锁
if (cnt % 3 != worker.idx) worker.condition.await();
if (cnt < 100) System.out.println(worker + ":" + cnt++);
// 通知别人抢锁
worker.nextCondition.signal();
if (cnt >= 100) break;
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
}