ArrayBlockingQueue是一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部是在队列中存在时间最长的元素。队列的尾部是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。

ArrayBlockingQueue继承自 AbstractQueue并实现 BlockingQueue接口。

ArrayBlockingQueue是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞,试图从空队列中提取元素将导致类似阻塞。

ArrayBlockingQueue支持对等待的生产者线程和使用者线程进行排序的可选公平策略。默认情况下,不保证是这种排序。然而,通过将公平性 (fairness) 设置为 true 而构造的队列允许按照 FIFO 顺序访问线程。公平性通常会降低吞吐量,但也减少了可变性和避免了“不平衡性”。 公平性通过创建 ArrayBlockingQueue实例时指定。

1.成员变量

  1. /** 队列数组实现 */
  2. private final E[] items;
  3. /** 已取出元素索引,用于下一个元素的 take, poll or remove */
  4. private int takeIndex;
  5. /** 已插入元素索引,用于下一个元素的 put, offer, or add */
  6. private int putIndex;
  7. /** 队列中项目数 */
  8. private int count;
  9. /** 保护所有访问的主锁 */
  10. private final ReentrantLock lock;
  11. /** Condition 实例,用于等待中 take */
  12. private final Condition notEmpty;
  13. /** Condition 实例,用于等待中 put*/
  14. private final Condition notFull;

之前我们已经学习了锁相关的知识,所以几个成员变量不难理解。

1)其中E[] items;是数组式的队列实现;

2)takeIndex 用于记录 take操作的次数;

3)putIndex 用于记录 put操作的次数;

4)count 用于记录队列中元素数目;

5)ReentrantLock lock 是用于控制访问的主锁;

6)Condition notEmpty 获取操作时的条;

7)Condition notFull 插入操作时的条件;

2.构造方法

ArrayBlockingQueue的构造方法有3个。

1)最简单的构造方法:

  1. //指定队列大小
  2. public ArrayBlockingQueue(int capacity) {
  3. this(capacity, false);
  4. }

此种构造方法最为简单也最为常用,在创建 ArrayBlockingQueue实例时只需指定其大小即可。

  1. BlockingQueue<Object> q = new ArrayBlockingQueue<Object>(10);

2)增加访问策略的构造方法,除了指定队列大小外还可指定队列的访问策略:

  1. //指定队列大小、访问策略
  2. public ArrayBlockingQueue(int capacity, boolean fair) {
  3. if (capacity <= 0)
  4. throw new IllegalArgumentException();
  5. this.items = (E[]) new Object[capacity];
  6. lock = new ReentrantLock(fair);
  7. notEmpty = lock.newCondition();
  8. notFull = lock.newCondition();
  9. }

fair如果为 true,则按照 FIFO 顺序访问插入或移除时受阻塞线程的队列;如果为 false,则访问顺序是不确定的。

构造方法中首先初始化了 items数组,然后根据fair创建 ReentrantLock实例,最后返回 notEmpty与 notFull两个 Condition实例,分别用于等待中的获取与添加操作。

3)带初始元素的构造方法:

  1. //指定队列大小、访问策略、初始元素
  2. public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c) {
  3. this(capacity, fair);
  4. if (capacity < c.size())
  5. throw new IllegalArgumentException();
  6. for (Iterator<? extends E> it = c.iterator(); it.hasNext();)
  7. add(it.next());
  8. }

除了可以指定容量和访问策略外,还可以包含给定 collection 的元素,并以 collection 迭代器的遍历顺序添加元素。

代码也可以观察到,在实例化队列之后还使用了add方法将 collection 中的元素按原有顺序添加到实例中。

        3.添加元素

1)add方法

ArrayBlockingQueue的add方法调用的是父类方法,而父类 add方法则调用的是 offer方法,以下是add方法的源代码:

  1. public boolean add(E e) {
  2. //调用父类add方法
  3. return super.add(e);
  4. }

2)offer方法

offer方法将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则返回 false。此方法通常要优于 add(E) 方法,后者可能无法插入元素,而只是抛出一个异常。

  1. /**
  2. * 将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),
  3. * 在成功时返回 true,如果此队列已满,则返回 false。
  4. * 此方法通常要优于 add(E) 方法,后者可能无法插入元素,而只是抛出一个异常。
  5. */
  6. public boolean offer(E e) {
  7. //判断e是否为null
  8. if (e == null)
  9. throw new NullPointerException();
  10. final ReentrantLock lock = this.lock;
  11. //获取锁
  12. lock.lock();
  13. try {
  14. //判断队列是否已满
  15. if (count == items.length)
  16. return false;
  17. else {
  18. //如果未满则插入
  19. insert(e);
  20. return true;
  21. }
  22. } finally {
  23. //释放锁
  24. lock.unlock();
  25. }
  26. }

因为 add方法在队列已满时会抛出异常,所以 offer方法一般优于 add方法使用。

首先,判断要添加的元素是否为 null,如果为null则抛出空指针异常。

接着,创建一个 ReentrantLock实例,ReentrantLock是可重入锁实现。更详细介绍参考http://286.iteye.com/blog/2296191

然后,获取锁。

最后,判断队列是否已满,如果已满则返回 false;如果未满则调用insert方法插入元素,返回true。

所有操作完成后释放锁。

offer方法的处理流程可以参照以下流程图:


        从代码中就可以看到,offer方法利用了ReentrantLock来实现队列阻塞的功能,所以多线程操作相同队列时会排队等待。

offer的另一个重载方法是 offer(E e, long timeout, TimeUnit unit),此重载方法将指定的元素插入此队列的尾部,如果该队列已满,则在到达指定的等待时间之前等待可用的空间。其源代码为:

  1. /**
  2. * 将指定的元素插入此队列的尾部,如果该队列已满,则在到达指定的等待时间之前等待可用的空间。
  3. */
  4. public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException {
  5. if (e == null)
  6. throw new NullPointerException();
  7. long nanos = unit.toNanos(timeout);
  8. final ReentrantLock lock = this.lock;
  9. lock.lockInterruptibly();
  10. try {
  11. for (;;) {
  12. if (count != items.length) {
  13. insert(e);
  14. return true;
  15. }
  16. if (nanos <= 0)
  17. return false;
  18. try {
  19. nanos = notFull.awaitNanos(nanos);
  20. } catch (InterruptedException ie) {
  21. notFull.signal(); // propagate to non-interrupted thread
  22. throw ie;
  23. }
  24. }
  25. } finally {
  26. lock.unlock();
  27. }
  28. }

与普通offer方法不同之处在于:offer(E e, long timeout, TimeUnit unit)方法利用循环在指定时间内不断去尝试添加元素,如果成功则返回true,如果指定时间已到则退出返回 false。

3)insert方法

insert方法在当前位置(putIndex)插入元素。

  1. /**
  2. * 在当前位置(putIndex)插入元素(在获得锁的情况下调用)
  3. */
  4. private void insert(E x) {
  5. //设置 putIndex位置 items数组元素为x
  6. items[putIndex] = x;
  7. //返回putIndex新值,如果已满则返回0,未满则+1
  8. putIndex = inc(putIndex);
  9. //增加元素数量
  10. ++count;
  11. //唤醒获取线程
  12. notEmpty.signal();
  13. }

因为 ArrayBlockingQueue内部队列实现为数组items,而 putIndex则记录了队列中已添加元素的位置,所以新添加的元素就直接被添加到数组的指定位置。随后修改 putIndex值,如果队列未满则+1,如果已满则从0重新开始。最后唤醒 notEmpty中的一个线程。

4)put方法

将指定的元素插入此队列的尾部,如果该队列已满,则等待可用的空间。

以下是put方法的源代码:

  1. /**
  2. * 将指定的元素插入此队列的尾部,如果该队列已满,则等待可用的空间
  3. */
  4. public void put(E e) throws InterruptedException {
  5. //判断e是否为null
  6. if (e == null)
  7. throw new NullPointerException();
  8. final E[] items = this.items;
  9. final ReentrantLock lock = this.lock;
  10. //获取中断锁
  11. lock.lockInterruptibly();
  12. try {
  13. try {
  14. //利用循环判断队列是否已满
  15. while (count == items.length)
  16. //如果已满则调用await方法阻塞等待
  17. notFull.await();
  18. } catch (InterruptedException ie) {
  19. notFull.signal(); // propagate to non-interrupted thread
  20. throw ie;
  21. }
  22. //队列未满则插入
  23. insert(e);
  24. } finally {
  25. //释放锁
  26. lock.unlock();
  27. }
  28. }

put方法与其他方法类似,其中会循环判断队列是否已满,如果已满则阻塞 notFull,如果未满则调用 insert方法添加元素。因为其中运用了循环判断队列是否有位置添加新元素,如果队列已满则产生阻塞等待,直至可以添加元素为止。

将本文开始时的例子修改一下,去掉消费者,只留下生产者,这样当队列满了之后没有消费者去消费产品,生产者就不会再向队列中插入了:

  1. class Producer implements Runnable {
  2. private final ArrayBlockingQueue<Integer> queue;
  3. private int i;
  4. Producer(ArrayBlockingQueue<Integer> q) {
  5. queue = q;
  6. }
  7. public void run() {
  8. try {
  9. while (true) {
  10. int p=produce();
  11. queue.put(p);// 将产品放入缓冲队列
  12. System.out.println("插入成功:"+p);
  13. }
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. int produce() {
  19. return i++;// 生产产品
  20. }
  21. }
  22. public class Runner {
  23. public static void main(String[] args) {
  24. ArrayBlockingQueue<Integer> q = new ArrayBlockingQueue<Integer>(10);
  25. Producer p = new Producer(q);
  26. new Thread(p).start();
  27. }
  28. }
  29. //结果:
  30. 插入成功:0
  31. 插入成功:1
  32. 插入成功:2
  33. 插入成功:3
  34. 插入成功:4
  35. 插入成功:5
  36. 插入成功:6
  37. 插入成功:7
  38. 插入成功:8
  39. 插入成功:9

此时程序并不会退出,而是阻塞在那里等待队列有位置插入。

4.获取元素

1)peek方法

获取但不移除此队列的头;如果此队列为空,则返回 null。以下是peek方法的源代码:

  1. /**
  2. * 获取但不移除此队列的头;如果此队列为空,则返回 null
  3. */
  4. public E peek() {
  5. final ReentrantLock lock = this.lock;
  6. //获取锁
  7. lock.lock();
  8. try {
  9. //判断队列中是否有元素,如果没有则返回null,如果存在则返回该元素
  10. return (count == 0) ? null : items[takeIndex];
  11. } finally {
  12. //释放锁
  13. lock.unlock();
  14. }
  15. }

peek代码比较简单,首先判断队列是否有元素,即count==0,如果为空则返回null,非空则返回相应元素。

2)poll方法

获取并移除此队列的头,如果此队列为空,则返回 null。以下是poll方法的源代码:

  1. /**
  2. * 获取并移除此队列的头,如果此队列为空,则返回 null
  3. */
  4. public E poll() {
  5. final ReentrantLock lock = this.lock;
  6. //获取锁
  7. lock.lock();
  8. try {
  9. //判断是否存在元素
  10. if (count == 0)
  11. return null;
  12. //调用extract方法返回元素
  13. E x = extract();
  14. return x;
  15. } finally {
  16. //释放锁
  17. lock.unlock();
  18. }
  19. }

poll方法调用的是 extract()方法来获取头元素。

3)extract方法

以下是extract()方法的源代码:

  1. /**
  2. * 从 takeIndex位置获取元素(在获得锁的情况下调用)
  3. */
  4. private E extract() {
  5. final E[] items = this.items;
  6. //获取元素
  7. E x = items[takeIndex];
  8. //移除原位置元素
  9. items[takeIndex] = null;
  10. //计算 takeIndex新值
  11. takeIndex = inc(takeIndex);
  12. --count;
  13. //唤醒添加线程
  14. notFull.signal();
  15. return x;
  16. }

4)take方法

获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)。

  1. /**
  2. * 获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)
  3. */
  4. public E take() throws InterruptedException {
  5. final ReentrantLock lock = this.lock;
  6. // 获取中断锁
  7. lock.lockInterruptibly();
  8. try {
  9. try {
  10. // 如果队列未空则阻塞等待,直到有元素为止
  11. while (count == 0)
  12. notEmpty.await();
  13. } catch (InterruptedException ie) {
  14. notEmpty.signal(); // 唤醒获取线程
  15. throw ie;
  16. }
  17. // 调用extract方法返回元素
  18. E x = extract();
  19. return x;
  20. } finally {
  21. // 释放锁
  22. lock.unlock();
  23. }
  24. }

与put方法类似,take方法也是利用循环阻塞的方式来获取元素,如果没有元素则等待,直至获取元素为止。

与put方法的例子类似,生产者只生产5个产品,消费完这5个产品后,消费者就不得不等待队列有元素可取:

  1. class Producer implements Runnable {
  2. private final ArrayBlockingQueue<Integer> queue;
  3. private int i;
  4. Producer(ArrayBlockingQueue<Integer> q) {
  5. queue = q;
  6. }
  7. public void run() {
  8. try {
  9. for (int i = 0; i < 5; i++) {
  10. int p = produce();
  11. queue.put(p);// 将产品放入缓冲队列
  12. System.out.println("插入成功:" + p);
  13. }
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. int produce() {
  19. return i++;// 生产产品
  20. }
  21. }
  22. class Consumer implements Runnable {
  23. private final ArrayBlockingQueue<Integer> queue;
  24. Consumer(ArrayBlockingQueue<Integer> q) {
  25. queue = q;
  26. }
  27. public void run() {
  28. try {
  29. while (true) {
  30. int p = queue.take();
  31. System.out.println("获取成功:" + p);
  32. }
  33. } catch (InterruptedException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. void consume(Object x) {
  38. System.out.println("消费:" + x);// 消费产品
  39. }
  40. }
  41. public class Runner {
  42. public static void main(String[] args) {
  43. ArrayBlockingQueue<Integer> q = new ArrayBlockingQueue<Integer>(10);// 或其他实现
  44. Producer p = new Producer(q);
  45. Consumer c1 = new Consumer(q);
  46. Consumer c2 = new Consumer(q);
  47. new Thread(p).start();
  48. new Thread(c1).start();
  49. new Thread(c2).start();
  50. }
  51. }
  52. //结果:
  53. 插入成功:0
  54. 插入成功:1
  55. 插入成功:2
  56. 插入成功:3
  57. 插入成功:4
  58. 获取成功:0
  59. 获取成功:1
  60. 获取成功:2
  61. 获取成功:3
  62. 获取成功:4

之后程序也是会阻塞在那里。

5.移除元素

1)remove方法

remove方法从此队列中移除指定元素的单个实例(如果存在)。更确切地讲,如果此队列包含一个或多个满足 o.equals(e) 的元素 e,则移除该元素。如果此队列包含指定的元素(或者此队列由于调用而发生更改),则返回 true。

  1. /**
  2. * 从此队列中移除指定元素的单个实例(如果存在)
  3. */
  4. public boolean remove(Object o) {
  5. //判断要移除元素是否为空
  6. if (o == null)
  7. return false;
  8. final E[] items = this.items;
  9. final ReentrantLock lock = this.lock;
  10. //获取锁
  11. lock.lock();
  12. try {
  13. int i = takeIndex;
  14. int k = 0;
  15. for (;;) {
  16. //判断队列是否含有元素
  17. if (k++ >= count)
  18. return false;
  19. //比较
  20. if (o.equals(items[i])) {
  21. //移除
  22. removeAt(i);
  23. return true;
  24. }
  25. //返回i新值,以便下次循环使用
  26. i = inc(i);
  27. }
  28. } finally {
  29. //释放锁
  30. lock.unlock();
  31. }
  32. }

remove方法其中利用循环来不断判断该元素的位置,如果找到则调用 removeAt方法移除指定位置的数组元素。

以下是一个移除的小例子:

  1. ArrayBlockingQueue<Integer> q = new ArrayBlockingQueue<Integer>(10);
  2. // 添加10个元素
  3. for (int i = 0; i < 10; i++) {
  4. q.add(i);
  5. }
  6. // 移除值为 1,3,5,7,9 的这五个元素
  7. q.remove(1);
  8. q.remove(3);
  9. q.remove(5);
  10. q.remove(7);
  11. q.remove(9);
  12. //又移除了一次 9
  13. q.remove(9);
  14. for (Integer i : q) {
  15. System.out.println(i);
  16. }
  17. //结果:
  18. 0
  19. 2
  20. 4
  21. 6
  22. 8

从结果可以看出,从队列中正确的移除了我们指定的元素,在最后即使指定已经不存在的元素值,remove方法也之后返回false。

2)drainTo方法

drainTo方法用于移除此队列中所有可用的元素,并将它们添加到给定 collection 中。此操作可能比反复轮询此队列更有效。在试图向 collection c 中添加元素没有成功时,可能导致在抛出相关异常时,元素会同时在两个 collection 中出现,或者在其中一个 collection 中出现,也可能在两个 collection 中都不出现。如果试图将一个队列放入自身队列中,则会导致 IllegalArgumentException 异常。此外,如果正在进行此操作时修改指定的 collection,则此操作行为是不确定的。

以下是 drainTo方法的源代码:

  1. public int drainTo(Collection<? super E> c) {
  2. //如果指定 collection为 null 抛出异常
  3. if (c == null)
  4. throw new NullPointerException();
  5. //如果指定 collection 是此队列,或者此队列元素的某些属性不允许将其添加到指定 collection 抛出异常
  6. if (c == this)
  7. throw new IllegalArgumentException();
  8. final E[] items = this.items;
  9. final ReentrantLock lock = this.lock;
  10. //获取锁
  11. lock.lock();
  12. try {
  13. //take操作的位置
  14. int i = takeIndex;
  15. int n = 0;
  16. //元素数量
  17. int max = count;
  18. //利用循环不断取出元素添加到c中
  19. while (n < max) {
  20. c.add(items[i]);
  21. items[i] = null;
  22. i = inc(i);
  23. ++n;
  24. }
  25. //添加完成后初始化必要值,唤醒添加线程
  26. if (n > 0) {
  27. count = 0;
  28. putIndex = 0;
  29. takeIndex = 0;
  30. notFull.signalAll();
  31. }
  32. //返回添加元素数量
  33. return n;
  34. } finally {
  35. //释放锁
  36. lock.unlock();
  37. }
  38. }

代码中并没有添加失败的相关处理,所以结果如上所说并不一定完整。以下是相关实例:

  1. ArrayBlockingQueue<Integer> q = new ArrayBlockingQueue<Integer>(10);
  2. List<Integer> list = new ArrayList<Integer>();
  3. // 添加10个元素
  4. for (int i = 0; i < 10; i++) {
  5. q.add(i);
  6. list.add(i + 10);
  7. }
  8. //将q中的元素添加到 list中
  9. q.drainTo(list);
  10. for (Integer i : list) {
  11. System.out.println(i);
  12. }
  13. //结果:
  14. 10
  15. 11
  16. 12
  17. 13
  18. 14
  19. 15
  20. 16
  21. 17
  22. 18
  23. 19
  24. 0
  25. 1
  26. 2
  27. 3
  28. 4
  29. 5
  30. 6
  31. 7
  32. 8
  33. 9

需要值得注意的是 drainTo方法是将队列中的元素按顺序添加到指定 Collection中,别弄反了。

drainTo(Collection<? super E> c, int maxElements)用法类似,只不过指定了移除元素数。

3)clear方法

移除此队列中的所有元素。在此调用返回之后,队列将为空。

  1. /**
  2. * 移除此队列中的所有元素。在此调用返回之后,队列将为空
  3. */
  4. public void clear() {
  5. final E[] items = this.items;
  6. final ReentrantLock lock = this.lock;
  7. //获取锁
  8. lock.lock();
  9. try {
  10. int i = takeIndex;
  11. int k = count;
  12. //将数组元素置为null
  13. while (k-- > 0) {
  14. items[i] = null;
  15. i = inc(i);
  16. }
  17. //初始化其他参数
  18. count = 0;
  19. putIndex = 0;
  20. takeIndex = 0;
  21. //唤醒添加线程
  22. notFull.signalAll();
  23. } finally {
  24. //释放锁
  25. lock.unlock();
  26. }
  27. }

clear方法比较简单,就是利用循环清除数组中的元素,然后将相关参数置为初始值。

ArrayBlockingQueue还有一些其他方法,这些方法相对简单这里就不细说了。

Java并发之ArrayBlockingQueue的更多相关文章

  1. 深入理解Java并发之synchronized实现原理

    深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoader) 深入 ...

  2. JAVA并发之阻塞队列浅析

    背景 因为在工作中经常会用到阻塞队列,有的时候还要根据业务场景获取重写阻塞队列中的方法,所以学习一下阻塞队列的实现原理还是很有必要的.(PS:不深入了解的话,很容易使用出错,造成没有技术深度的样子) ...

  3. java并发之Future与Callable使用

    java并发之Future与Callable使用 这篇文章需要大家知道线程.线程池的知识,尤其是线程池. 有的时候我们要获取线程的执行结果,这个时候就需要用到Callable.Future.Futur ...

  4. java并发之固定对象与实例

    java并发之固定对象与实例 Immutable Objects An object is considered immutable if its state cannot change after ...

  5. Java并发之BlockingQueue的使用

    Java并发之BlockingQueue的使用 一.简介 前段时间看到有些朋友在网上发了一道面试题,题目的大意就是:有两个线程A,B,  A线程每200ms就生成一个[0,100]之间的随机数, B线 ...

  6. Java并发之Semaphore的使用

    Java并发之Semaphore的使用 一.简介 今天突然发现,看着自己喜欢的球队发挥如此的棒,然后写着博客,这种感觉很爽.现在是半场时间,就趁着这个时间的空隙,说说Java并发包中另外一个重量级的类 ...

  7. Java并发之CyclicBarria的使用(二)

    Java并发之CyclicBarria的使用(二) 一.简介 之前借助于其他大神写过一篇关于CyclicBarria用法的博文,但是内心总是感觉丝丝的愧疚,因为笔者喜欢原创,而不喜欢去转载一些其他的文 ...

  8. Java并发之CyclicBarria的使用

    Java并发之CyclicBarria的使用 一.简介 笔者在写CountDownLatch这个类的时候,看到了博客园上的<浅析Java中CountDownLatch用法>这篇博文,为博主 ...

  9. Java并发之CountDownLatch的使用

    Java并发之CountDownLatch的使用 一. 简介 Java的并发包早在JDK5这个版本中就已经推出,而且Java的并发编程是几乎每个Java程序员都无法绕开的屏障.笔者今晚在家闲来无事,翻 ...

随机推荐

  1. Asp.net Mvc使用PagedList分页

    git:https://github.com/troygoode/PagedList 1. Nuget 安装package watermark/2/text/aHR0cDovL2Jsb2cuY3Nkb ...

  2. uva 12627 - Erratic Expansion(递归求解)

    递归的边界条件写的多了--不是必需写呢么多的.. 不明确可共同探讨~ #include<cstdio> #include<iostream> #include<cmath ...

  3. 解决java.math.BigDecimal divide方法运算结果为无限小数问题

    http://samueli.iteye.com/blog/224755 BigDecimal除法运算报错,错误如下:Non-terminating decimal expansion; no exa ...

  4. 利用CFAbsoluteTimeGetCurrent()计算时间差

    开发中,遇到计算时间差的问题,利用CFAbsoluteTimeGetCurrent()可以很方便的进行计算 实例: 场景:类似购物车中修改商品数量的功能,如下图所示,要求,修改完的数量,要同步到服务器 ...

  5. ios 不通过import 调用其他控制器的方法

    ios 开发过程中在不通过import 调用其他类的方法 //获取类名 Class controller = NSClassFromString(@"controller"); / ...

  6. c#利用委托传递函数参数(1)

    本次旨在解决 同参不同名 的函数作为参数传递的情况 情景: 一下两个函数分别多次重复调用了两个同参不同名的函数(实际上总共有3个这样的函数),函数结构基本相同,只有调用的函数名不一样,显然可以整合在一 ...

  7. ASP.NET MVC深入浅出(被替换) 第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式 第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery ) 第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法 第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性) 第十节: EF的三种追踪

    ASP.NET MVC深入浅出(被替换)   一. 谈情怀-ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态 ...

  8. CSS 温故而知新 断句失败

    设置了一定的宽度和高度.但无论是下面哪句都无效. word-break: break-word; word-wrap: break-word; 原因竟然是因为 /* white-space: nowr ...

  9. TouchSlide - 大话主席

    http://www.superslide2.com/TouchSlide/downLoad.html 首  页如何使用查看参数案例演示下载页面交流反馈SuperSlide TouchSlide - ...

  10. Java编程之路相关书籍(三个维度)

    一.关于Java的技术学习.能够依照以下分三个维度进行学习 : (1)向下发展,也就是底层的方向 建议看<深入Java虚拟机>.<Java虚拟机规范>.<Thinking ...