关于互斥锁:

所谓互斥锁, 指的是一次最多只能有一个线程持有的锁. 在jdk1.5之前, 我们通常使用synchronized机制控制多个线程对共享资源的访问. 而现在, Lock提供了比synchronized机制更广泛的锁定操作, Lock和synchronized机制的主要区别:

synchronized机制提供了对与每个对象相关的隐式监视器锁的访问, 并强制所有锁获取和释放均要出现在一个块结构中, 当获取了多个锁时, 它们必须以相反的顺序释放. synchronized机制对锁的释放是隐式的, 只要线程运行的代码超出了synchronized语句块范围, 锁就会被释放. 而Lock机制必须显式的调用Lock对象的unlock()方法才能释放锁, 这为获取锁和释放锁不出现在同一个块结构中, 以及以更自由的顺序释放锁提供了可能.

关于可重入

一、2.4.1 内部锁

Java 提供了原子性的内置锁机制: sychronized 块。它包含两个部分:锁对象的引用和这个锁保护的代码块:

synchronized(lock) {

// 访问或修改被锁保护的共享状态

}

内部锁扮演了互斥锁( mutual exclusion lock, 也称作 mutex )的角色,一个线程拥有锁的时候,别的线程阻塞等待。

2.4.2 重进入(Reentrancy )

重入性:指的是同一个线程多次试图获取它所占有的锁,请求会成功。当释放锁的时候,直到重入次数清零,锁才释放完毕。

Public class Widget {

Public synchronized void doSomething(){

}

}

Public class LoggingWidget extends Widget {

Public synchronized void doSomething(){

System.out.println(toString()+”:calling doSomething”);

Super.doSomething();

}

}

二、一般来说,在多线程程序中,某个任务在持有某对象的锁后才能运行任务,其他任务只有在该任务释放同一对象锁后才能拥有对象锁,然后执行任务。于是,想到,同一个任务在持有同一个对象的锁后,在不释放锁的情况下,继续调用同一个对象的其他同步(synchronized)方法,该任务是否会再次持有该对象锁呢?

答案是肯定的。同一个任务在调用同一个对象上的其他synchronized方法,可以再次获得该对象锁。

  1. synchronized  m1(){
  2. //加入此时对锁a的计数是N
  3. m2();  //进入m2的方法体之后锁计数是N+1,离开m2后是N
  4. }
  5. synchronized m2(){}

同一任务和对象锁的问题:http://www.iteye.com/topic/728485

  1. /*public class ReentrantLock
  2. extends Object implements Lock, Serializable
  3. */

一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。

ReentrantLock 将由最近成功获得锁,并且还没有释放该锁的线程所拥有。当锁没有被另一个线程所拥有时,调用 lock 的线程将成功获取该锁并返回。如果当前线程已经拥有该锁,此方法将立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。

此类的构造方法接受一个可选的公平 参数。当设置为 true 时,在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程。否则此锁将无法保证任何特定访问顺序。与采用默认设置(使用不公平锁)相比,使用公平锁的程序在许多线程访问时表现为很低的总体吞吐量(即速度很慢,常常极其慢),但是在获得锁和保证锁分配的均衡性时差异较小。不过要注意的是,公平锁不能保证线程调度的公平性。因此,使用公平锁的众多线程中的一员可能获得多倍的成功机会,这种情况发生在其他活动线程没有被处理并且目前并未持有锁时。还要注意的是,未定时的 tryLock 方法并没有使用公平设置。因为即使其他线程正在等待,只要该锁是可用的,此方法就可以获得成功。

JDK:http://www.xasxt.com/java/api/java/util/concurrent/locks/ReentrantLock.html

  1. /*构造方法摘要
  2. ReentrantLock()
  3. 创建一个 ReentrantLock 的实例。
  4. ReentrantLock(boolean fair)
  5. 创建一个具有给定公平策略的 ReentrantLock。
  6. */
  1. /**public void lock()
  2. 获取锁。
  3. 如果该锁没有被另一个线程保持,则获取该锁并立即返回,将锁的保持计数设置为 1。
  4. 如果当前线程已经保持该锁,则将保持计数加 1,并且该方法立即返回。
  5. 如果该锁被另一个线程保持,则出于线程调度的目的,禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态,此时锁保持计数被设置为 1。
  6. */

ReentrantLock 的lock机制有2种,忽略中断锁和响应中断锁,这给我们带来了很大的灵活性。比如:如果A、B 2个线程去竞争锁,A线程得到了锁,B线程等待,但是A线程这个时候实在有太多事情要处理,就是 一直不返回,B线程可能就会等不及了,想中断自己,不再等待这个锁了,转而处理其他事情。这个时候ReentrantLock就提供了2种机制,第一,B线程中断自己(或者别的线程中断它),但是ReentrantLock 不去响应,继续让B线程等待,你再怎么中断,我全当耳边风(synchronized原语就是如此);第二,B线程中断自己(或者别的线程中断它),ReentrantLock 处理了这个中断,并且不再等待这个锁的到来,完全放弃。请看例子:

Example1:

  1. package test;
  2. public interface IBuffer {
  3. public void write();
  4. public void read() throws InterruptedException;
  5. }

使用Synchronized:

  1. package test;
  2. public class Buffer implements IBuffer {
  3. private Object lock;
  4. public Buffer() {
  5. lock = this;
  6. }
  7. public void write() {
  8. synchronized (lock) {
  9. long startTime = System.currentTimeMillis();
  10. System.out.println("开始往这个buff写入数据…");
  11. for (;;)// 模拟要处理很长时间
  12. {
  13. if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE)
  14. break;
  15. }
  16. System.out.println("终于写完了");
  17. }
  18. }
  19. public void read() {
  20. synchronized (lock) {
  21. System.out.println("从这个buff读数据");
  22. }
  23. }
  24. }

使用ReentrantLock:

  1. package test;
  2. import java.util.concurrent.locks.ReentrantLock;
  3. public class BufferInterruptibly implements IBuffer {
  4. private ReentrantLock lock = new ReentrantLock();
  5. public void write() {
  6. lock.lock();
  7. try {
  8. long startTime = System.currentTimeMillis();
  9. System.out.println("开始往这个buff写入数据…");
  10. for (;;)// 模拟要处理很长时间
  11. {
  12. if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE)
  13. break;
  14. }
  15. System.out.println("终于写完了");
  16. } finally {
  17. lock.unlock();
  18. }
  19. }
  20. public void read() throws InterruptedException{
  21. lock.lockInterruptibly();// 注意这里,可以响应中断
  22. try {
  23. System.out.println("从这个buff读数据");
  24. } finally {
  25. lock.unlock();
  26. }
  27. }
  28. }

测试类(注意那两个线程不是内部类!):

  1. package test;
  2. public class Test {
  3. //是用ReentrantLock,还是用synchronized
  4. public static boolean useSynchronized = false;
  5. public static void main(String[] args) {
  6. IBuffer buff = null;
  7. if(useSynchronized){
  8. buff = new Buffer();
  9. }else{
  10. buff = new BufferInterruptibly();
  11. }
  12. final Writer writer = new Writer(buff);
  13. final Reader reader = new Reader(buff);
  14. writer.start();
  15. reader.start();
  16. new Thread(new Runnable() {
  17. public void run() {
  18. long start = System.currentTimeMillis();
  19. for (;;) {
  20. // 等5秒钟去中断读
  21. if (System.currentTimeMillis() - start > 5000) {
  22. System.out.println("不等了,尝试中断");
  23. reader.interrupt();
  24. break;
  25. }
  26. }
  27. }
  28. }).start();
  29. }
  30. }
  31. class Writer extends Thread {
  32. private IBuffer buff;
  33. public Writer(IBuffer buff) {
  34. this.buff = buff;
  35. }
  36. @Override
  37. public void run() {
  38. buff.write();
  39. }
  40. }
  41. class Reader extends Thread {
  42. private IBuffer buff;
  43. public Reader(IBuffer buff) {
  44. this.buff = buff;
  45. }
  46. @Override
  47. public void run() {
  48. try {
  49. buff.read();
  50. } catch (InterruptedException e) {
  51. System.out.println("我不读了");
  52. }
  53. System.out.println("读结束");
  54. }
  55. }

结果:

使用ReentrantLock时:

开始往这个buff写入数据…

不等了,尝试中断

我不读了

读结束

使用Synchronized时:

开始往这个buff写入数据…

不等了,尝试中断

实例来源:http://blog.csdn.net/quqi99/article/details/5298017

实例2:

http://junlas.iteye.com/blog/846460

实例3:

http://www.blogjava.net/killme2008/archive/2007/09/14/145195.html

重要:

一个证明可中断的例子:http://yanxuxin.iteye.com/blog/566713

关于多线程问题,signalAll,await问题:http://www.iteye.com/problems/72378

ReentrantLock :http://hujin.iteye.com/blog/479689

java的concurrent用法详解:

http://www.open-open.com/bbs/view/1320131360999

ReentrantLock-互斥同步器:

http://www.cnblogs.com/mandela/archive/2011/04/08/2009810.html

一个重要Example:

  1. package tags;
  2. import java.util.Calendar;
  3. public class TestLock {
  4. private ReentrantLock lock = null;
  5. public int data = 100;     // 用于线程同步访问的共享数据
  6. public TestLock() {
  7. lock = new ReentrantLock(); // 创建一个自由竞争的可重入锁
  8. }
  9. public ReentrantLock getLock() {
  10. return lock;
  11. }
  12. public void testReentry() {
  13. lock.lock();
  14. Calendar now = Calendar.getInstance();
  15. System.out.println(now.getTime() + " " + Thread.currentThread() + " get lock.");
  16. }
  17. public static void main(String[] args) {
  18. TestLock tester = new TestLock();
  19. //1、测试可重入
  20. tester.testReentry();
  21. tester.testReentry(); // 能执行到这里而不阻塞,表示锁可重入
  22. tester.testReentry(); // 再次重入
  23. // 释放重入测试的锁,要按重入的数量解锁,否则其他线程无法获取该锁。
  24. tester.getLock().unlock();
  25. tester.getLock().unlock();
  26. tester.getLock().unlock();
  27. //2、测试互斥
  28. // 启动3个线程测试在锁保护下的共享数据data的访问
  29. new Thread(new workerThread(tester)).start();
  30. new Thread(new workerThread(tester)).start();
  31. new Thread(new workerThread(tester)).start();
  32. }
  33. // 线程调用的方法
  34. public void testRun() throws Exception {
  35. lock.lock();
  36. Calendar now = Calendar.getInstance();
  37. try {
  38. // 获取锁后显示 当前时间 当前调用线程 共享数据的值(并使共享数据 + 1)
  39. System.out.println(now.getTime() + " " + Thread.currentThread()+ " accesses the data " + data++);
  40. Thread.sleep(1000);
  41. } catch (Exception e) {
  42. e.printStackTrace();
  43. } finally {
  44. lock.unlock();
  45. }
  46. }
  47. }
  48. // 工作线程,调用TestServer.testRun
  49. class workerThread implements Runnable {
  50. private TestLock tester = null;
  51. public workerThread(TestLock testLock) {
  52. this.tester = testLock;
  53. }
  54. public void run() {
  55. try {
  56. tester.testRun();
  57. } catch (Exception e) {
  58. e.printStackTrace();
  59. }
  60. }
  61. }

Example3:

  1. package tags;
  2. import java.util.concurrent.locks.ReentrantLock;
  3. public class ReentrantLockSample {
  4. public static void main(String[] args) {
  5. testSynchronized();
  6. //testReentrantLock();
  7. }
  8. public static void testReentrantLock() {
  9. final SampleSupport1 support = new SampleSupport1();
  10. Thread first = new Thread(new Runnable() {
  11. public void run() {
  12. try {
  13. support.doSomething();
  14. }
  15. catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. }
  19. });
  20. Thread second = new Thread(new Runnable() {
  21. public void run() {
  22. try {
  23. support.doSomething();
  24. }
  25. catch (InterruptedException e) {
  26. System.out.println("Second Thread Interrupted without executing counter++,beacuse it waits a long time.");
  27. }
  28. }
  29. });
  30. executeTest(first, second);
  31. }
  32. public static void testSynchronized() {
  33. final SampleSupport2 support2 = new SampleSupport2();
  34. Runnable runnable = new Runnable() {
  35. public void run() {
  36. support2.doSomething();
  37. }
  38. };
  39. Thread third = new Thread(runnable);
  40. Thread fourth = new Thread(runnable);
  41. executeTest(third, fourth);
  42. }
  43. /**
  44. * Make thread a run faster than thread b,
  45. * then thread b will be interruted after about 1s.
  46. * @param a
  47. * @param b
  48. */
  49. public static void executeTest(Thread a, Thread b) {
  50. a.start();
  51. try {
  52. Thread.sleep(100);
  53. b.start(); // The main thread sleep 100ms, and then start the second thread.
  54. Thread.sleep(1000);
  55. // 1s later, the main thread decided not to allow the second thread wait any longer.
  56. b.interrupt();
  57. }
  58. catch (InterruptedException e) {
  59. e.printStackTrace();
  60. }
  61. }
  62. }
  63. abstract class SampleSupport {
  64. protected int counter;
  65. /**
  66. * A simple countdown,it will stop after about 5s.
  67. */
  68. public void startTheCountdown() {
  69. long currentTime = System.currentTimeMillis();
  70. for (;;) {
  71. long diff = System.currentTimeMillis() - currentTime;
  72. if (diff > 5000) {
  73. break;
  74. }
  75. }
  76. }
  77. }
  78. class SampleSupport1 extends SampleSupport {
  79. private final ReentrantLock lock = new ReentrantLock();
  80. public void doSomething() throws InterruptedException {
  81. lock.lockInterruptibly(); // (1)
  82. System.out.println(Thread.currentThread().getName() + " will execute counter++.");
  83. startTheCountdown();
  84. try {
  85. counter++;
  86. }
  87. finally {
  88. lock.unlock();
  89. }
  90. }
  91. }
  92. class SampleSupport2 extends SampleSupport {
  93. public synchronized void doSomething() {
  94. System.out.println(Thread.currentThread().getName() + " will execute counter++.");
  95. startTheCountdown();
  96. counter++;
  97. }
  98. }

在这个例子中,辅助类SampleSupport提供一个倒计时的功能startTheCountdown(),这里倒计时5s左右。SampleSupport1,SampleSupport2继承其并分别的具有doSomething()方法,任何进入方法的线程会运行5s左右之后counter++然后离开方法释放锁。SampleSupport1是使用ReentrantLock机制,SampleSupport2是使用synchronized机制。

testSynchronized()和testReentrantLock()都分别开启两个线程执行测试方法executeTest(),这个方法会让一个线程先启动,另一个过100ms左右启动,并且隔1s左右试图中断后者。结果正如之前提到的第二点:interrupt()对于synchronized是没有作用的,它依然会等待5s左右获得锁执行counter++;而ReentrantLock机制可以保证在线程还未获得并且试图获得锁时如果发现线程中断,则抛出异常清除中断标记退出竞争。所以testReentrantLock()中second线程不会继续去竞争锁,执行异常内的打印语句后线程运行结束。

来源:http://yanxuxin.iteye.com/blog/566713

Example4:

三个线程,线程名分别为A、B、C,设计程序使得三个线程循环打印“ABC”10次后终止。如:ABCABCABCABCABCABCABCABCABCABC

  1. package tags;
  2. import java.util.concurrent.locks.ReentrantLock;
  3. public class ReentrantLockPractice {
  4. static ReentrantLock lock = new ReentrantLock();
  5. private static String[] threadArr = {"A","B","C"};
  6. public static void main(String[] args){
  7. ReentrantLockPractice pc = new ReentrantLockPractice();
  8. pc.startDemo();
  9. }
  10. void startDemo(){
  11. for(int i = 0;i<10;i++){
  12. for(String name : threadArr){
  13. TestThread t = new TestThread(name);
  14. t.start();
  15. try {
  16. Thread.sleep(100);
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. }
  22. }
  23. class TestThread extends Thread{
  24. //自定义线程名字
  25. TestThread(String str){
  26. super(str);
  27. }
  28. public void run(){
  29. try {
  30. lock.lockInterruptibly();
  31. System.out.print(Thread.currentThread().getName());
  32. } catch (InterruptedException e) {
  33. e.printStackTrace();
  34. } finally{
  35. lock.unlock();
  36. }
  37. }
  38. }
  39. }

注意与Example2的区别,一个线材类定义在内部,一个在外部,注意区别。

其他方法:

http://hxraid.iteye.com/blog/607228

相同:ReentrantLock提供了synchronized类似的功能和内存语义。

不同:

1.ReentrantLock功能性方面更全面,比如时间锁等候,可中断锁等候,锁投票等,因此更有扩展性。在多个条件变量和高度竞争锁的地方,用ReentrantLock更合适,ReentrantLock还提供了Condition,对线程的等待和唤醒等操作更加灵活,一个ReentrantLock可以有多个Condition实例,所以更有扩展性。

2.ReentrantLock必须在finally中释放锁,否则后果很严重,编码角度来说使用synchronized更加简单,不容易遗漏或者出错。

3.ReentrantLock 的性能比synchronized会好点。

4.ReentrantLock提供了可轮询的锁请求,他可以尝试的去取得锁,如果取得成功则继续处理,取得不成功,可以等下次运行的时候处理,所以不容易产生死锁,而synchronized则一旦进入锁请求要么成功,要么一直阻塞,所以更容易产生死锁。

1、Lock的某些方法可以决定多长时间内尝试获取锁,如果获取不到就抛异常,这样就可以一定程度上减轻死锁的可能性。

如果锁被另一个线程占据了,synchronized只会一直等待,很容易错序死锁

2、synchronized的话,锁的范围是整个方法或synchronized块部分;而Lock因为是方法调用,可以跨方法,灵活性更大 

3、便于测试,单元测试时,可以模拟Lock,确定是否获得了锁,而synchronized就没办法了

ReentrantLock比synchronized 强大在哪儿?

简单说:

1、ReentrantLock可以实现fair lock 

public ReentrantLock(boolean fair) {

sync = (fair)? new FairSync() : new NonfairSync();

}

所谓fair lock就是看获得锁的顺序是不是和申请锁的时间的顺序是一致的 

2、ReentrantLock支持中断处理 

public final void acquireInterruptibly(int arg) throws InterruptedException {

if (Thread.interrupted())

throw new InterruptedException();

if (!tryAcquire(arg))

doAcquireInterruptibly(arg);

}

就是说那些持有锁的线程一直不释放,正在等待的线程可以放弃等待。

3、ReentrantLock可以和condition结合使用

public boolean hasWaiters(Condition condition) {

if (condition == null)

throw new NullPointerException();

if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))

throw new IllegalArgumentException("not owner");

return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);

}

public int getWaitQueueLength(Condition condition) {

if (condition == null)

throw new NullPointerException();

if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))

throw new IllegalArgumentException("not owner");

return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);

}

内置锁synchronized

显式锁Lock

ReentrantLock代码剖析之ReentrantLock.lock

ReentrantLock中tryLock的使用问题(注意循环)

synchronized是可重入锁

如果一个获取锁的线程调用其它的synchronized修饰的方法,会发生什么?

从设计上讲,当一个线程请求一个由其他线程持有的对象锁时,该线程会阻塞。当线程请求自己持有的对象锁时,如果该线程是重入锁,请求就会成功,否则阻塞。

我们回来看synchronized,synchronized拥有强制原子性的内部锁机制,是一个可重入锁。因此,在一个线程使用synchronized方法时调用该对象另一个synchronized方法,即一个线程得到一个对象锁后再次请求该对象锁,是永远可以拿到锁的。

在Java内部,同一个线程调用自己类中其他synchronized方法/块时不会阻碍该线程的执行,同一个线程对同一个对象锁是可重入的,同一个线程可以获取同一把锁多次,也就是可以多次重入。原因是Java中线程获得对象锁的操作是以线程为单位的,而不是以调用为单位的。

synchronized可重入锁的实现

每个锁关联一个线程持有者和一个计数器。当计数器为0时表示该锁没有被任何线程持有,那么任何线程都都可能获得该锁而调用相应方法。当一个线程请求成功后,JVM会记下持有锁的线程,并将计数器计为1。此时其他线程请求该锁,则必须等待。而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增。当线程退出一个synchronized方法/块时,计数器会递减,如果计数器为0则释放该锁。

ReentrantLock synchronized的更多相关文章

  1. synchronized 和 ReentrantLock 区别

    synchronized 使用: 1:当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁.结果,其它线程对该object对象所有同步代 ...

  2. 死磕 java同步系列之ReentrantLock VS synchronized——结果可能跟你想的不一样

    问题 (1)ReentrantLock有哪些优点? (2)ReentrantLock有哪些缺点? (3)ReentrantLock是否可以完全替代synchronized? 简介 synchroniz ...

  3. Java进阶知识点:不要只会写synchronized - JDK十大并发编程组件总结

    一.背景 提到Java中的并发编程,首先想到的便是使用synchronized代码块,保证代码块在并发环境下有序执行,从而避免冲突.如果涉及多线程间通信,可以再在synchronized代码块中使用w ...

  4. Java进阶知识点7:不要只会写synchronized - JDK十大并发编程组件总结

    一.背景 提到Java中的并发编程,首先想到的便是使用synchronized代码块,保证代码块在并发环境下有序执行,从而避免冲突.如果涉及多线程间通信,可以再在synchronized代码块中使用w ...

  5. 用阻塞队列实现一个生产者消费者模型?synchronized和lock有什么区别?

    多线程当中的阻塞队列 主要实现类有 ArrayBlockingQueue是一个基于数组结构的有界阻塞队列,此队列按FIFO原则对元素进行排序 LinkedBlockingQueue是一个基于链表结构的 ...

  6. Java并发编程二三事

    Java并发编程二三事 转自我的Github 近日重新翻了一下<Java Concurrency in Practice>故以此文记之. 我觉得Java的并发可以从下面三个点去理解: * ...

  7. 201521123013 《Java程序设计》第11周学习总结

    1. 本章学习总结 2. 书面作业 Q1.1.互斥访问与同步访问 完成题集4-4(互斥访问)与4-5(同步访问) 1.1 除了使用synchronized修饰方法实现互斥同步访问,还有什么办法实现互斥 ...

  8. 201521044091 《Java程序设计》第11周学习总结

    1. 本章学习总结 2. 书面作业 Q1.1.互斥访问与同步访问完成题集4-4(互斥访问)与4-5(同步访问) 1.1 除了使用synchronized修饰方法实现互斥同步访问,还有什么办法实现互斥同 ...

  9. Java高并发--AQS

    Java高并发--AQS 主要是学习慕课网实战视频<Java并发编程入门与高并发面试>的笔记 AQS是AbstractQueuedSynchronizer的简称,直译过来是抽象队列同步器. ...

随机推荐

  1. Spring Boot + Jpa(Hibernate) 架构基本配置

    本文转载自:https://blog.csdn.net/javahighness/article/details/53055149 1.基于springboot-1.4.0.RELEASE版本测试 2 ...

  2. css display table使用小例子实验

    display的下面: table: 此元素会作为块级表格来显示(类似 <table>),表格前后带有换行符. table-row 此元素会作为一个表格行显示(类似 <tr>) ...

  3. python selenium-1 环境搭建new

    1.Mac安装Anaconda集成环境 集成环境有助于快速编写脚本. 将安装的python3版本设置为python,系统的python更改为python2 Anaconda地址 https://www ...

  4. 1073 Scientific Notation (20 分)

    1073 Scientific Notation (20 分) Scientific notation is the way that scientists easily handle very la ...

  5. Ubuntu 11.10 H3C iNode 客户端安装

    下载客户端,放到桌面 双击打开,点击解压缩 Ctrl+Alt+T打开终端,依次输入以下代码并回车 代码: cd 桌面sudo cp iNodeClient /usr -Rcd /usr/iNodeCl ...

  6. java中key-value数据有重复KEY如何存储

    http://www.iteye.com/problems/87219 Map<Key, List<Value>>, 这个好 师兄厉害,给介绍了个神器:guava

  7. Convolutional Neural Networks

    卷积神经网络(Convolutional Neural Networks/ CNN/ConvNets) 卷积神经网络和普通神经网络十分相似: 组成它们的神经元都具有可学习的权重(weights)和偏置 ...

  8. 关于封装Dll为Web Service技术方案的讨论

    关于web架构技术方案的讨论整理 Sonictl 2014年1月25日10:05:52 本着"三人行必有我师"的学习态度,我在近期跟x老师做了大量沟通,结合我们单位对于" ...

  9. js选择器 querySelector

    <form method="post" action="" id="myform"> <input type=" ...

  10. vue pm2守护进程

    Linux 创建一个.sh可执行脚本,例如hexo.sh 代码 12 #!/usr/bin/env bashhexo server 使用pm2 start hexo.sh执行脚本 Windows 创建 ...