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高并发比较经典的,推荐阅读,还有更重要的是,阅读的时候一定要把握书的脉络,这个个人感觉非常重要,否则学到的都是一个个的点,不成体系,很容易遗忘。另一点就是光看书是不够的,还要配合实践才能理解的深,目前我也还在学习这块知识中,共勉。

posted @ 2019-03-03 22:38  陈海翔  阅读(121)  评论(0编辑  收藏  举报