ARTS第十一周
ARTS第十一周
ARTS是什么?
Algorithm:每周至少做一个leetcode的算法题;
Review:阅读并点评至少一篇英文技术文章;
Tip/Techni:学习至少一个技术技巧;
Share:分享一篇有观点和思考的技术文章。
Algorithm
题目:232. Implement Queue using Stacks
解题思路
题目要求用栈实现队列的功能。对比栈和队列的特性差异:栈的特性是先进后出,队列是先进先出。我们要用先进后出的容器实现先进先出的效果,考虑使用两个先进后出的容器来实现这种效果,相当于负负得正的效果。
代码
class MyQueue {
//输入栈
Stack<Integer> input;
//输出栈
Stack<Integer> output;
/** Initialize your data structure here. */
public MyQueue() {
input = new Stack();
output = new Stack();
}
/** Push element x to the back of queue. */
public void push(int x) {
input.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
if (output.empty()) {
while (!input.empty()) {
output.push(input.pop());
}
}
return output.pop();
}
/** Get the front element. */
public int peek() {
if (output.empty()) {
while (!input.empty()) {
output.push(input.pop());
}
}
return output.peek();
}
/** Returns whether the queue is empty. */
public boolean empty() {
return input.empty() && output.empty();
}
}
Review
信号量Semaphore
计数信号量,概念上讲,一个信号量保持了一组许可。每个acquire方法都会阻塞直到拿到一个许可。每个release方法增加一个许可,可能会释放一个阻塞的acquirer.然而,没有实际的许可对象,Semaphore只是保持一个可用的计数并采取相应的行为。
信号量经常被用来限制线程数,而不是访问某些资源。比如下面的类,用了一个信号量控制访问池.
class Pool {
private static final int MAX_AVAILABLE = 100;
private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
public Object getItem() throws InterruptedException {
available.acquire();
return getNextAvailableItem();
}
public void putItem(Object x) {
if (markAsUnused(x))
available.release();
}
// Not a particularly efficient data structure; just for demo
protected Object[] items = ... whatever kinds of items being managed
protected boolean[] used = new boolean[MAX_AVAILABLE];
protected synchronized Object getNextAvailableItem() {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (!used[i]) {
used[i] = true;
return items[i];
}
}
return null; // not reached
}
protected synchronized boolean markAsUnused(Object item) {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (item == items[i]) {
if (used[i]) {
used[i] = false;
return true;
} else
return false;
}
}
return false;
}
}
在获取一项信号量之前每个线程必须从信号量获得许可,保证有一项可用的信号量。当线程用完这项信号量,许可将返回给信号量,允许另一个线程去获得这项信号量。注意,调用acquire方法的时候不会保持同步锁定,因为这会阻止信号量返回池中。信号量封装了限制访问池所需的同步,与维护池本身一致性所需的任何同步分开。
信号量初始化为1,并且使用它至多只有一个许可,能被用来做互斥锁。这通常被称为二进制信号量,因为它只有两种状态:1个可用的许可;0个可用的许可。当用这种方式,这个二进制信号量有一个属性:锁能被非拥有者释放,这能被用在一些特殊场景,比如死锁恢复。
这个类的构造函数可用接受一个fairness参数。当设置为false的时候,此类不保证线程获得许可的顺序。事实上,允许barging意味着,调用acquire方法的线程能先于已经在等待的线程被分配到一个许可,逻辑上,这个新线程排到等待线程队列的开头。当fairness设置成true,信号量保证调用acquire方法的线程按照他们的调用顺序获得许可。注意,FIFO顺序一定适用于这些方法的特定内部执行点。所以,一个线程可以在另一个线程之前调用acquire方法,但是在另一个线程之后到达排序点,并且从方法返回时类似。另外注意,不定时的tryAcquire方法不遵守公平性设置,但会接受任何可用的许可。
一般来说,信号量用来控制资源访问的时候应该被初始化为fair,为了确保访问某个资源的时候没有线程会饿死。当信号量用于其他类型的并发控制,非公平锁的吞吐量优势往往超过公平考量。
Tip/Techni
volatile关键字
作用:在多线程环境下,保证了共享变量的“可见性”。
原理:
1.将当前处理器缓存行数据写回到系统内存。
2.使其他cpu缓存了该内存地址的数据失效。
synchronized
作用:为修饰的对象加上锁,实现同步效果。
修饰对象:
1.普通同步方法:锁的是当前实例对象。
2.静态同步方法:锁的是当前类的class对象。
3.同步方法块:锁的是Synchronized括号里配置的对象。
原理:基于进入和退出monitor对象来实现方法同步和代码块同步。
Share
最近一周主要在学习java高并发,也找了一堆资料,在此分享关于一篇java高并发学习路线的文章Java并发编程学习路线,作者根据自己学习java高并发的经历总结了一条学习路线,我看完之后感觉学习体验和我类似,文中提到的基本书都是学习java高并发比较经典的,推荐阅读,还有更重要的是,阅读的时候一定要把握书的脉络,这个个人感觉非常重要,否则学到的都是一个个的点,不成体系,很容易遗忘。另一点就是光看书是不够的,还要配合实践才能理解的深,目前我也还在学习这块知识中,共勉。