1. Algorithm
上周开始学习并发编程,因此找了多线程的问题来练习,巩固知识。
1.1 1114. 按序打印
我们提供了一个类:
public class Foo {
public void one() { print("one"); }
public void two() { print("two"); }
public void three() { print("three"); }
}
三个不同的线程将会共用一个 Foo 实例。
线程 A 将会调用 one() 方法
线程 B 将会调用 two() 方法
线程 C 将会调用 three() 方法
请设计修改程序,以确保 two() 方法在 one() 方法之后被执行,three() 方法在 two() 方法之后被执行。
示例 1:
输入: [1,2,3]
输出: "onetwothree"
解释:
有三个线程会被异步启动。
输入 [1,2,3] 表示线程 A 将会调用 one() 方法,线程 B 将会调用 two() 方法,线程 C 将会调用 three() 方法。
正确的输出是 "onetwothree"。
示例 2:
输入: [1,3,2]
输出: "onetwothree"
解释:
输入 [1,3,2] 表示线程 A 将会调用 one() 方法,线程 B 将会调用 three() 方法,线程 C 将会调用 two() 方法。
正确的输出是 "onetwothree"。
注意:
尽管输入中的数字似乎暗示了顺序,但是我们并不保证线程在操作系统中的调度顺序。
你看到的输入格式主要是为了确保测试的全面性。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/print-in-order
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
当然可以有几种方法来实现,本周正好学到Lock
和Condition
,所以我的方案是条件变量
class Foo {
final Lock lock = new ReentrantLock();
// 标识first方法执行是否完成的条件
final Condition first = lock.newCondition();
// 标识second方法执行是否完成的条件
final Condition second = lock.newCondition();
// 标识当前运行到哪一个方法
private AtomicInteger flag = new AtomicInteger(0);
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
// 加锁
lock.lock();
try {
// printFirst.run() outputs "first". Do not change or remove this line.
// first方法是需要第一个运行的,因此不需要条件判断,上来运行就可以了
printFirst.run();
// 运行完成之后,标识下一个方法应该是2
flag.set(2);
// 通知first方法已经执行完成,可以运行唤醒下一个线程了
// 因为happens-before,所以执行first.singal()保证对flag的修改对下一个线程可见
first.signal();
} finally {
lock.unlock();
}
}
public void second(Runnable printSecond) throws InterruptedException {
lock.lock();
try {
// 判断上一个方法是否执行完成,如果没有,阻塞线程
while(flag.get() != 2) {
first.await();
}
// first.signal(); 调用后,
// printSecond.run() outputs "second". Do not change or remove this line.
printSecond.run();
// 同first()的处理
flag.set(3);
second.signal();
} finally {
lock.unlock();
}
}
public void third(Runnable printThird) throws InterruptedException {
lock.lock();
try {
while(flag.get() != 3) {
second.await();
}
// printThird.run() outputs "third". Do not change or remove this line.
printThird.run();
} finally {
lock.unlock();
}
}
}
之前看java并发编程课程的时候,还以为自己已经懂了,真正做题的时候才发现脑袋里有点空。反映在解题中就是一开始没有设置条件private AtomicInteger flag = new AtomicInteger(0);
导致死锁。
另外在写题解的时候我也是有点疑惑于flag.set(2);
和while(flag.get() != 2)
条件之间是否由于Happens-before规则而保证在调用first.signal()
之后才对second()
方法可见。因此调整了flag.set(2)
的顺序,结果是一致的,说明我的理解没有错,在lock.lock()
之后的代码块内确实遵循Happens-before
规则。
用信号量也可以实现
class Foo {
final Semaphore s2 = new Semaphore(0);
final Semaphore s3 = new Semaphore(0);
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
printFirst.run();
s2.release();
}
public void second(Runnable printSecond) throws InterruptedException {
s2.acquire();
printSecond.run();
s3.release();
}
public void third(Runnable printThird) throws InterruptedException {
s3.acquire();
printThird.run();
}
}
同样也找到了知识盲点,Semaphore
中的acquire()
方法对应计数器减1,执行此方法后如果计数器小于0则会阻塞当前线程,还是看了评论区的答案后才恍然大悟。
2. Review
2.1 Continuous Delivery - It’s Not All about Tech!
背景介绍
作者的职业生涯从开发者到软件测试再到如今是NewVoiceMedia公司的工程经理(engineering manager)。
这篇文章讲述的是非技术因素在持续集成中的影响,包括:
- 仔细评估需求某些在上线前加急的需求是否必要,砍掉那些非真正紧急的需求将使得发布更加稳定;
- 事前仔细研究,事后复盘,最好使用可视化的手段来展现当前的瓶颈;
- 使用Toyota Improvement Kata
3. Tip
3.1 在构造方法中抛出异常
我们会遇到一些情况当创建对象时需要检查参数有效性,比如:
public class Person {
private int age;
public Person(int _age) {
if (_age < 0) {
throw new Exception();
}
this.age = _age;
}
}
那么在构造方法中抛出异常是一个好办法么?
参考: https://stackoverflow.com/questions/6086334/is-it-good-practice-to-make-the-constructor-throw-an-exception
3.2 预测试提交、个人构建、试飞构建
这是CI/CD中的概念,预测试提交(pre-tested commit)、个人构建(personal build)、试飞构建(preflight build)其实讲的是同一件事,就是本地提交会自动测试是否构建通过,如果通过了持续集成服务器才会将变更提交到版本控制库中。
Jenkins中的插件介绍中有相关策略介绍(https://plugins.jenkins.io/pretested-integration/)
4. Share: 慢下来的能力
我是在2014年的时候知道Python
这门编程语言的。当时人工智能还处在爆发的前夕,远远没有到现在万众瞩目,言必称AI、机器学习的程度。因此Python
这门语言还是处在推荐的人很多,讲了许多优秀的地方,但是暂时没有看到一个杀手级应用的状况。
因为工作中很少有机会用到,我也就没有认真去学,后来AI大爆发,但凡订阅一点技术公众号,看个帖子都是Python
培训班的广告。
现在我时常使用Python
解决工作中的一些问题,通过点滴积累发现自己也能开始写点有用的东西了。如果这件事在2014年就开始做的话,那么到今天有了5、6年的积累我也能熬成一个Python
中级程序员了,而不是一边写一边查资料的菜鸟。
但是现在不写现在不学的话也就永远不会。技术更新迭代太快,看准一个之后一定要去试试,不要急。