lock学习笔记02

Intel LOCK前缀指令 《卷二:指令集》 LOCK—Assert LOCK# Signal Prefix Description Causes the processor’s LOCK# signal to be asserted during execution of the accompanying instruction (turns the instruction into an atomic instru... 阅读详情

lock实现ReentrantLock分析

ReentrantLock 是 JUC 中提供的可中断, 可重入获取, 支持超时, 支持尝试获取锁

ReentrantLock 内部封装了成员变量 private final Sync sync;

1. lock操作

1.1 非公平锁 ,获取锁的方式是随机竞争的,新来的线程也有机会抢占锁

 public ReentrantLock() { sync = new NonfairSync();}

源码解析:

一些重要的点我提出来说一下(先看源码):

1. 非公平锁体现在新来的线程(还未加入到同步阻塞队列中)是有机会与队列中的线程争抢锁的,但是同步阻塞队列本质上还是一个FIFO队列。进入队列后节点调用acquireQueued方法让自己阻塞或者尝试获取锁,但是不是每个节点都有获取锁的资格,acquireQueued方法还保证了只有头节点的后继节点才有资格去争抢锁。当线程获取了锁之后,意味着原先的头节点(可能是初始化队列时的傀儡空节点)就会从这个同步队列中移除。同时还会设置获取同步状态的此线程为头节点。

2. 我们都知道等待队列中的线程节点是无法争抢锁的,它们必须先被唤醒,然后加入到同步队列中。同时,同步阻塞队列中的线程节点并不是在不停的尝试争抢锁,而是处于阻塞状态。那么,什么时候应该被阻塞park,什么时候应该解除阻塞unpark(这里为了避免误解,用 “解除阻塞” 而不是 “唤醒”)。线程进入阻塞队列后的逻辑在acquireQueued方法中。

3. 为什么必须前驱结点为头结点才尝试去获取锁?因为头结点表示当前正占有锁的线程(或者未无意义的空节点),正常情况下该线程释放锁后会通知后面结点中阻塞的线程(查看锁释放流程源码),阻塞线程被唤醒后去获取锁,这是我们希望看到的。然而还有一种情况,就是前驱结点取消了等待,此时当前线程也会被唤醒,这时候就不应该去获取锁,而是往前回溯一直找到一个没有取消等待的结点,然后将自身连接在它后面。一旦我们成功获取了锁并成功将自身设置为头结点,就会跳出for循环。否则就会执行第二个if分句:确保前驱结点的状态为SIGNAL,然后阻塞当前线程。

4. 同步队列中,线程在阻塞自己前必须设置前驱结点的状态为SIGNAL,否则它不会阻塞自己,同时阻塞的线程都是由前驱节点进行唤醒(解除阻塞)的。

final void lock() {	
     /* 新来的线程通过CAS方式尝试将自己状态置为加锁状态(具体实现由JVM完成,cas可以自行了解)
        这里是非公平模式实现要点,这样做主要是为了新来的线程和排队中的线程竞争,哪个线程抢的    
        快,哪个就能拿到锁 */
	if (compareAndSetState(0, 1))
		// 如果设置加锁状态成功,则设置AbstractQueuedSynchronizer中的全局变量线程为当前当前线程
		setExclusiveOwnerThread(Thread.currentThread());
	else
		// 如果CAS快速获取锁方式失败,则执行通用获取锁逻辑。
		acquire(1);
}
public final void acquire(int arg) {
	// 调用tryAcquire(arg)获取锁
	if (!tryAcquire(arg) &&
// 如果获取失败,则调用addWaiter方法进行排队操作,将当期线程构造成节点,加入到同步阻塞队列中,
// 这里Node.EXCLUSIVE常量为null。之后调用acquireQueued方法进行自旋获取锁操作
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
		selfInterrupt();
}
// 这里涉及到ReetrantLock的结构,自行看源码即可。此处调用的是非公平锁的实现
protected final boolean tryAcquire(int acquires) {
	return nonfairTryAcquire(acquires);
}

final boolean nonfairTryAcquire(int acquires) {
	final Thread current = Thread.currentThread();
	int c = getState();
	// 为0表示当前状态未加锁
	if (c == 0) {
		// 继续尝试加锁
		if (compareAndSetState(0, acquires)) {
			setExclusiveOwnerThread(current);
			return true;
		}
	}
	// 已经加锁,且当前正在执行的线程是此线程,则表示这是一次锁重入
	else if (current == getExclusiveOwnerThread()) {
		int nextc = c + acquires;
		if (nextc < 0) // overflow
			throw new Error("Maximum lock count exceeded");
		// 更新锁状态值
		setState(nextc);
		return true;
	}
	return false;
}
private Node addWaiter(Node mode) {
	// 将当前线程构造成新的节点,其后置节点为mode,这里是null
	Node node = new Node(Thread.currentThread(), mode);
	Node pred = tail;
	if (pred != null) { // 队尾节点不为空,则从尾部入队
		// 设置前置节点
		node.prev = pred;
		// cas快速添加到尾部
		if (compareAndSetTail(pred, node)) {
			pred.next = node;
			return node;
		}
	}
	// 队尾节点为空(队列还未初始化)或上述方式不成功,则调用enq方法入队
	enq(node);
	return node;
}

        /**
	 * 创建以当前线程为基础的节点,先走快速添加到尾部逻辑,获取尾节点如果尾节点存在,将当前节点和尾节点相连,
	 * 并用CAS方式将当前节点设置为尾节点,这边使用CAS方式考虑了多个线程同时操作尾节点的情况,
	 * 所以如果尾节点已经变更则快速添加节点操作失败,调用enq(node)方法走最大努力添加节点的逻辑。
	 * enq(node)最大努力添加逻辑就是一直添加节点直到添加节点到尾部成功。
	 */


private Node enq(final Node node) {
	// 常规的队列加入操作,死循环直到成功加入
	for (;;) {
		Node t = tail;
		if (t == null) { // Must initialize
			// **** 如果头尾节点为空则创建一个傀儡空节点当头尾节点,用以初始化队列
			if (compareAndSetHead(new Node()))
				tail = head;
		} else {
			node.prev = t;
			if (compareAndSetTail(t, node)) {
				t.next = node;
				return t;
			}
		}
	}
}
// 线程加入同步队列后的运行逻辑
final boolean acquireQueued(final Node node, int arg) {
    // 这个循环的主要作用就是在线程激活后尝试获取锁直到成功
	boolean failed = true;
	try {
		boolean interrupted = false;
		for (;;) {
			// 获取其前置节点
			final Node p = node.predecessor();
			// 只有当前节点的前置节点为头结点时,当前节点才有资格进行尝试获取锁操作
			if (p == head && tryAcquire(arg)) {
        //在设置头节点的过程中不需要任何的同步操作,因为独占式锁中能获取同步状态的必定是同一个线程。
				setHead(node);
        // 从队列中删除原头节点。原头节点的前置节点为null,thred为null,将next也设为null方便GC
				p.next = null; // help GC
				failed = false;
				return interrupted;
			}
        // 判断是否需要阻塞等待
			if (shouldParkAfterFailedAcquire(p, node) &&
        // 如果需要阻塞等待则调用parkAndCheckInterrupt()阻塞此线程不再尝试,并让出cup资源直到被前一个节点激活
					parkAndCheckInterrupt())
				interrupted = true;
		}
	} finally {
		if (failed)
			cancelAcquire(node);
	}
}


 private void setHead(Node node) {
        head = node;
        node.thread = null;
        node.prev = null;
    }
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
//判断前置线程节点的状态是否是SIGNAL状态,如果是则表示前置线程节点已经被通知,这样当前线程就可以阻塞安心的等待上个节点的激活
        if (ws == Node.SIGNAL)
            return true;
// 状态为CANCELLED,则一直往队列头部回溯直到找到一个状态不为CANCELLED的结点,将当前节点node挂在这个结点的后面
        if (ws > 0) {
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {  // 这里也就意味着状态为0,是初始状态(头节点的状态),则通过csa将状态置为SIGNAL
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

其实这个方法的含义很简单,就是确保当前结点的前驱结点的状态为SIGNAL,SIGNAL意味着线程释放锁后会唤醒后面阻塞的线程。毕竟,只有确保能够被唤醒,当前线程才能放心的阻塞。

但是要注意只有在前驱结点已经是SIGNAL状态后才会返回true刻执行acquireQueued后面的步骤,立即阻塞,
对应上面的第一种情况。其他两种情况则因为返回false而重新执行一遍for循环。这种延迟阻塞其实也是一种
高并发场景下的优化,试想我如果在重新执行循环的时候成功获取了锁,是不是线程阻塞唤醒的开销就省了呢?


private final boolean parkAndCheckInterrupt() {
       LockSupport.park(this);
       return Thread.interrupted();
}

(以下参考https://www.cnblogs.com/takumicx/p/9402021.html)

为什么基于FIFO的同步队列可以实现非公平锁?

由FIFO队列的特性知,先加入同步队列等待的线程会比后加入的线程更靠近队列的头部,那么它将比后者更早的被唤醒,它也就能更早的得到锁。从这个意义上,对于在同步队列中等待的线程而言,它们获得锁的顺序和加入同步队列的顺序一致,这显然是一种公平模式。然而,线程并非只有在加入队列后才有机会获得锁,哪怕同步队列中已有线程在等待,非公平锁的不公平之处就在于此。回看下非公平锁的加锁流程,线程在进入同步队列等待之前有两次抢占锁的机会:

  • 第一次是非重入式的获取锁,只有在当前锁未被任何线程占有(包括自身)时才能成功;
  • 第二次是在进入同步队列前,包含所有情况的获取锁的方式。

只有这两次获取锁都失败后,线程才会构造结点并加入同步队列等待。而线程释放锁时是先释放锁(修改state值),然后才唤醒后继结点的线程的。试想下这种情况,线程A已经释放锁,但还没来得及唤醒后继线程C,而这时另一个线程B刚好尝试获取锁,此时锁恰好不被任何线程持有,它将成功获取锁而不用加入队列等待。线程C被唤醒尝试获取锁,而此时锁已经被线程B抢占,故而其获取失败并继续在队列中等待。

为什么非公平锁性能好

非公平锁在高并发情况下会出现的饥饿问题,在锁竞争激烈的情况下,在队列中等待的线程可能迟迟竞争不到锁。但是考虑性能原因,非公平锁使用的还是很多。

非公平锁对锁的竞争是抢占式的(队列中线程除外),线程在进入等待队列前可以进行两次尝试,这大大增加了获取锁的机会。这种好处体现在两个方面:

  • 1.线程不必加入等待队列就可以获得锁,不仅免去了构造结点并加入队列的繁琐操作,同时也节省了线程阻塞唤醒的开销,线程阻塞和唤醒涉及到线程上下文的切换和操作系统的系统调用,是非常耗时的。在高并发情况下,如果线程持有锁的时间非常短,短到线程入队阻塞的过程超过线程持有并释放锁的时间开销,那么这种抢占式特性对并发性能的提升会更加明显。

  • 2.减少CAS竞争。如果线程必须要加入阻塞队列才能获取锁,那入队时CAS竞争将变得异常激烈,CAS操作虽然不会导致失败线程挂起,但不断失败重试导致的对CPU的浪费也不能忽视。除此之外,加锁流程中至少有两处通过将某些特殊情况提前来减少CAS操作的竞争,增加并发情况下的性能。一处就是获取锁时将非重入的情况提前,如下图所示

另一处就是入队的操作,将同步队列非空的情况提前处理

这两部分的代码在之后的通用逻辑处理中都有,很显然属于重复代码,但因为避免了执行无意义的流程代码,比如for循环,获取同步状态等,高并发场景下也能减少CAS竞争失败的可能。


1.2  公平锁,获取锁的方式是按照线程加锁的顺序来分配的,先来先得FIFO

 public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync();}

公平锁的代码实现就不一一贴出来了,对比非公平锁主,要区别在于两个方法

1. 缺少了第一次的cas方式获取锁。

 final void lock() {
            this.acquire(1);
        }

2. tryAcqyure方法,新增了 判断队列中是否有优先级更高的等待线程 

        protected final boolean tryAcquire(int var1) {
            Thread var2 = Thread.currentThread();
            int var3 = this.getState();
            if (var3 == 0) {
                // 判断队列中是否存在优先级更高的线程节点
                if (!this.hasQueuedPredecessors() && this.compareAndSetState(0, var1)) {
                    this.setExclusiveOwnerThread(var2);
                    return true;
                }
            } else if (var2 == this.getExclusiveOwnerThread()) {
                int var4 = var3 + var1;
                if (var4 < 0) {
                    throw new Error("Maximum lock count exceeded");
                }

                this.setState(var4);
                return true;
            }

            return false;
        }
    }
public final boolean hasQueuedPredecessors() {
    // The correctness of this depends on head being initialized
    // before tail and on head.next being accurate if the current
    // thread is first in queue.
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

由于头结点是当前获取锁的线程,队列中的第二个结点代表的线程优先级最高。
那么我们只要判断队列中第二个结点是否存在以及这个结点是否代表当前线程就行了。这里分了两种情况进行探讨:

  1. 第二个结点已经完全插入,但是这个结点是否就是当前线程所在结点还未知,所以通过s.thread != Thread.currentThread()进行判断,如果为true,说明第二个结点代表其他线程。
  2. 第二个结点并未完全插入,我们知道结点入队一共分三步:
  • 1.待插入结点的pre指针指向原尾结点
  • 2.CAS更新尾指针
  • 3.原尾结点的next指针指向新插入结点

所以(s = h.next) == null 就是用来判断2刚执行成功但还未执行3这种情况的。这种情况第二个结点必然属于其他线程。
以上两种情况都会使该方法返回true,即当前有优先级更高的线程在队列中等待,那么当前线程将不会执行CAS操作去获取锁,保证了线程获取锁的顺序与加入同步队列的顺序一致,很好的保证了公平性,但也增加了获取锁的成本。

 


2. unlock操作

不区分公平锁与非公平锁,内容相对也比较简单。

public void unlock() {sync.release(1);}

public final boolean release(int arg) {
    // 确保锁(包括重入的情况)都释放
	if (tryRelease(arg)) {
		Node h = head;
// 锁释放完成后,如果头节点不为null,且状态值不等于0,则执行解除阻塞方法(>0意味着CANCELLD取消状
// 态,<0这里应该只能是SIGNAL,即-1状态,意味着下个节点正在阻塞等待。等于0说明还没被下个节点线程
// 调用shouldParkAfterFailedAcquire(p, node)方法设置成SIGNAL状态,也就说明下个节点线程还没被 
// 阻塞)通知阻塞队列中的某个线程解除阻塞(规则定义在unparkSuccessor中)
		if (h != null && h.waitStatus != 0)
			unparkSuccessor(h);
		return true;
	}
	return false;
}


protected final boolean tryRelease(int releases) {
	int c = getState() - releases;
	if (Thread.currentThread() != getExclusiveOwnerThread())
		throw new IllegalMonitorStateException();
	boolean free = false;
    // 所以对于重入锁的情况,重入多少次就需要手动释放多少次
	if (c == 0) {
		free = true;
		setExclusiveOwnerThread(null);
	}
	setState(c);
	return free;
}


  private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
// 一般情况下只要唤醒后继结点的线程就行了,但是后继结点可能已经取消等待,所以从队列尾部往前回溯,找到离头结点最近的正常结点,并唤醒其线程。
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
		    //激活线程
            LockSupport.unpark(s.thread);
    }

3. 其他:

lock源码分析参考文章 https://www.cnblogs.com/takumicx/p/9402021.html

关于LockSupport的简单了解来自于https://www.cnblogs.com/takumicx/p/9328459.html

一个lock创建多个Condition,也就是说可以拥有多个等待条件队列,但是只有一个同步队列。


demo(多生产者多消费者模式)

在多生产者多消费者模式中,synchronized加锁方式,在执行通知时,需要调用notifyAll方法唤醒所有的消费者与生产者(这其实是额外的开销,最好的情况是通知某一个生产者或者某个消费者),而不能用notify方法(因为可能会造成生产者唤醒生产者,这种现象累积多了,就会导致唤醒的全是生产者,最终程序假死)。使用lock/condtion机制可以利用两个Condition分别控制生产者线程和消费者线程。简易代码如下:

package pc4;

import pc2.MyThread;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author LiFeng
 * @create 2019-10-11 上午 11:07
 * @describe
 */
public class LockTest2 {
	public static void main(String[] args) {
		Resource res = new Resource();
		MyThreadA[] threadAS = new MyThreadA[10];
		MyThreadB[] threadBS = new MyThreadB[10];
		for (int i = 0; i < 3; i++) {
			threadAS[i] = new MyThreadA(res, i);
			threadAS[i].start();
			threadBS[i] = new MyThreadB(res, i);
			threadBS[i].start();
		}
	}
}

class Resource {
	private Lock lock = new ReentrantLock();
	private Condition pc = lock.newCondition();
	private Condition cc = lock.newCondition();
	private boolean hasValue = false;

	public void set() {
		try {
			lock.lock();
			while (hasValue) {
				System.out.println(Thread.currentThread().getName() + " | set wait");
				pc.await();
			}
			System.out.println(Thread.currentThread().getName() + " | set");
			hasValue = true;
			cc.signal();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	public void get() {
		try {
			lock.lock();
			while (!hasValue) {
				System.out.println(Thread.currentThread().getName() + " | get wait");
				cc.await();
			}
			System.out.println(Thread.currentThread().getName() + " | get");
			hasValue = false;
			pc.signal();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

}

class MyThreadA extends Thread {
	private Resource res;

	MyThreadA(Resource res, int i) {
		this.res = res;
		setName("s" + i);
	}

	@Override
	public void run() {
		super.run();
		for (int i = 0; i < Integer.MAX_VALUE; i++) {
			res.set();
		}
	}
}

class MyThreadB extends Thread {
	private Resource res;

	MyThreadB(Resource res, int i) {
		this.res = res;
		setName("g" + i);
	}

	@Override
	public void run() {
		super.run();
		for (int i = 0; i < Integer.MAX_VALUE; i++) {
			res.get();
		}
	}
}
[内核同步]自旋锁spin_lock、spin_lock_irq 和 spin_lock_irqsave 分析 在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋(特别浪费处理器时间),所以自旋锁不应该被持有时间过长。如果需要长时间锁定的话, 最好使用信号量。简单来说,自旋锁在内核中主要用来防止多处理器中并发访问临界区,防止内核抢占造成的竞争。:如果自旋锁锁住以后进入睡眠,而此时又不能进行处理器抢占(锁住会disable prempt),其他进程无法获得cpu,这样也不能唤醒睡眠的自旋锁,因此不相应任何操作。:自旋锁是一种轻量级的互斥锁,可以更高效的对互斥资源进行保护。 阅读详情

相关推荐

R语言实战:非正态数据下的Scheirer–Ray–Hare检验全流程解析

本文详细解析了在R语言中执行Scheirer–Ray–Hare检验的全流程。当数据不满足正态分布假设时,该非参数检验是传统双因素方差分析的有效替代方案。文章从原理、R环境配置、数据准备、代码实现、结果解读到事后比较与可视化,提供了完整的实战指南,帮助研究者稳健地分析多因素实验设计中的主效应和交互效应。

cc789的博客 406

Java ReentrantLock锁

1. 什么是ReentrantLock? ReentrantLock称为可重入锁,或者中心锁。 ReentrantLock加锁后需要手动解锁。 2.ReentrantLock的使用 方法 含义 备注 lock 加锁 加锁以后不会自动解锁,需要手动解锁(unlock)。当一个线程lock以后,其他线程lock时,会加入等待队列。 unlock 解锁 释放锁,和一般和lock成对使用 tryLock 尝试申请锁 尝试加锁,申请失败不加入等待

joedan0104的专栏 333

科研党必备:用Python+Excel批量下载文献的保姆级教程(附常见错误解决)

本文提供了一套利用Python和Excel实现外文文献批量下载的自动化解决方案。教程详细介绍了如何从数据库导出并清洗DOI信息,编写健壮的Python脚本进行批量下载,并包含错误处理与进阶优化技巧,旨在帮助科研人员将重复性劳动自动化,大幅提升文献收集效率。

weixin_29050513的博客 310

Java并发-AQS及各种Lock锁的原理

Java并发-AQS及各种Lock锁的原理 转载。 https://blog.csdn.net/zhangdong2012/article/details/79983404 什么是AQS AQS是AbustactQueued...

160

四足机械臂避坑指南:从URDF配置到逆运动学控制的完整流程

本文为四足机械臂(Loco-Manipulation)系统开发提供了一份详实的实战避坑指南。文章深入剖析了从URDF模型配置、核心控制循环设计到逆运动学(IK)求解与奖励函数构建的完整流程,重点讲解了如何避免关节命名错误、惯性参数设置不当、IK求解不稳定等常见陷阱,并强调了利用collision_bbox等可视化工具进行高效调试的方法,旨在帮助工程师构建稳定、高效的四足机械臂控制系统。

weixin_42509507的博客 192

从2D到3D地图开发:手把手教你用Cesium+Three.js实现卫星轨迹可视化(含代码片段)

本文详细介绍了如何结合Cesium与Three.js实现从2D到3D的卫星轨迹可视化。通过对比传统Web GIS框架的局限,阐述了Cesium处理三维地理与轨道数据、Three.js负责高级图形渲染的分工优势,并提供了从环境搭建、坐标同步到动态粒子效果实现的完整代码实战指南。

nut55的博客 673

说一下AQS的原理?

面试官:今天我们来聊聊lock锁吧? 候选者:嗯嗯嗯,没问题 面试官:先问点简单的吧,刚睡醒,还是有点困的。 候选者:刚睡醒来面我干嘛?你就这态度? 面试官:哈?你刚说了什么? 候选者:没事,我没说话… 面试官:你知道什么叫做公平和非公平锁吗 候选者:公平锁指的就是:在竞争环境下,先到临界区的线程比后到的线程一定更快地获取得到锁 候选者:那非公平就很好理解了:先到临界区的线程未必比后到的线程更快地获取得到锁 面试官:如果让你实现的话,你怎么实现公平和非公平锁? 候选者:公平锁可以把竞争的

分享Java技术知识,共同成长进步! 938

HC32F460的GPIO重映射实战:如何用3步搞定SPI引脚冲突问题

本文详细介绍了HC32F460的GPIO重映射功能,通过三步操作解决SPI引脚冲突问题。文章解析了HC32F460的FuncGroup功能分组机制,提供了具体的寄存器配置步骤和验证技巧,并对比了与传统方案的性能优势,帮助工程师灵活配置引脚,优化硬件设计。

t4y5u6i7o的博客 920

阻塞队列中的线程协作(阻塞、唤醒、锁)

自己写一个阻塞队列 阻塞队列所谓一种队列,主要操作由两个put放入元素和take取出元素。所谓的阻塞就是当多个线程同时存取数据时,如果遇到队列为空或者队列为满时,会发生阻塞。 put:向队列中存入一个元素,如果已满,则阻塞当前线程,等待唤醒。如果正常存入了元素,那么唤醒其他阻塞的线程(有些执行take操作的线程因为队列为空而阻塞) take:从队列中取一个元素,如果队列为空,则阻塞当前线程,等待唤...

naget的博客 1258

微信商家转账报错“当前使用用户过多,请稍后重新确认收款”解决方案

当前使用用户过多,请稍后重新确认收款”这个错误,其实就是典型的参数误处理导致的假报错。记住一句话就行👇JSAPI 场景下的package参数不要做 UrlEncode。如果你也遇到这个问题,希望这篇文章能帮你少踩一个坑。

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 787

华为数字化转型之道 方法篇 第五章 视IT为投资,用产品思维管理IT

比较传统信息化和数字化转型下的IT系统特征,我们发现业务环境、IT能力、业务和IT的关系都发生了巨大的变化(见图5-1)从“管理系统”到“作业平台”。企业通常的做法是将线下的文档、流程线上化,IT系统是作为管理系统而存在的。而面向数字化,企业的业务作业、业务运营和决策、团队协同等业务动作已经和IT系统集成,IT系统逐步转变为作业系统。从“功能优先”到“体验优先”,如果要整体完成一项工作往往需要用户在不同系统间切换,费时费力。

沧海之巅的专栏 1095

基于AQS的Reentrant Lock 加解锁过程源码分析

说到线程同步,大家最熟悉的可能就是Synchronized,反而Reentrant Lock用的很少,而Synchronized使用起来也很方便,在方法上添加关键字,在方法体内添加同步块,但是随着时间的推移,使用了Synchronized了的线程因为争抢同一个资源导致阻塞,如果在一个时间段内拿到锁的线程长时间未释放,这些线程就会一直阻塞下去,于是Reentrant Lock出现了 JDK从1.5版本就提供了可扩展的Lock类,Reentrant Lock实现Lock类,内部基于AQS机制扩展了公平锁F.

瓦力-眼观世界 356

基于Python的新闻聚合与推荐系统:抓取多新闻网站内容并根据用户兴趣进行个性化推荐

本文介绍了如何使用Python构建一个新闻聚合与推荐系统,涵盖了从新闻数据抓取、用户兴趣建模到推荐算法实现的全过程。通过爬虫技术抓取新闻网站内容,使用TF-IDF、Word2Vec等算法进行内容分析,并通过推荐算法向用户推送个性化的新闻,最终帮助用户快速获取自己感兴趣的信息。你可以根据具体需求,进一步扩展和优化系统。例如,加入更多的推荐算法、优化爬虫的抓取效率、增强前端展示的互动性等。通过本文的指导,您应该能够构建一个完整的新闻聚合与推荐系统,并为用户提供个性化的新闻推荐服务。

2201_76125261的博客 1515

《尚硅谷高级技术之JUC高并发编程》学习笔记02——Lock接口

文章目录synchronizedsynchronized 关键字售票案例多线程编程步骤LockLock接口lock()newCondition()ReentrantLockReadWriteLock实现多线程的多种方式总结实现多线程的多种方式总结 synchronized synchronized 关键字 synchronized 是 Java 中的关键字,是一种同步锁。它修饰的对象有以下几种: 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号 {} 括起来的代码,作用的对象是调用这个

weixin_43301424的博客 783

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

JUC并发编程学习笔记-02-Synchronized和Lock区别

视频链接 //基本的卖票例子 /* 真正的多线程开发,公司中的开发,要降低耦合性 线程就是一个单独的资源类 ,没有任何附属操作 */ public class demo { public static void main(String[] args) { //并发:多个线程操作同一个资源类,把资源类丢入线程 Ticket ticket = new Ticket(); //FunctionalInterface 函数式接口,jdk1.8 lambda

weixin_43824233的博客 194

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

JUC学习笔记-02-lock锁Condition的使用

Condition的认识 Condition中的await()方法相当于Object的wait()方法 Condition中的signal()方法相当于Object的notify()方法 Condition中的signalAll()相当于Object的notifyAll()方法。 Condition能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition,唤醒不同的线程,从而实现业务之间的通信。 ReentrantLock锁的使用

小小学渣的博客 365

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

【JavaSE学习笔记】多线程02_Lock,死锁,等待唤醒机制,线程组和线程池,Timer定时器

多线程02 A.Lock 1)概述 上一章中,使用同步机制synchronized解决了线程的安全问题 但我们并没有看到具体的锁对象是谁,JDK5以后java提供了接口Lock Lock提供了比使用synchronized方法和语句可获得更广泛的锁定操作 由于该Lock接口不能实例化,提供了子实现类:ReentrankLock

love__nana 473

【JUC —— 02 synchronized & Lock】

在Java中,和Lock都是用于实现线程同步的机制。尽管它们都用于解决并发问题,但它们有不同的特性和使用场景。

m0_72328778的博客 1683

JUC02——Lock接口

一、Synchronized  虽然可以使用 synchronized 来定义方法,但 synchronized 并不属于方法定义的一部分,也就是说,synchronized 关键字不能被继承。如果在父类中的某个方法使用了 synchronized 关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上 synchronized 关键字才可以。当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此

如不來的博客 377

lock_guard和unique_lock

锁 锁用来在多线程访问同一个资源时防止数据竞险,保证数据的一致性访问。多线程本来就是为了提高效率和响应速度,但锁的使用又限制了多线程的并行执行,这会降低效率,但为了保证数据正确,不得不使用锁,它们就是这样纠缠。 主要讨论 c++11 中的两种锁:lock_guard 和 unique_lock。这两种锁都可以对std::mutex进行封装,实现RAII的效果。绝大多数情况下这两种锁是可以互相替代的,区别是unique_lock比lock_guard能提供更多的功能特性(但需要付出性能的一些代价) 结合锁进行

sinat_35945236的博客 9696

MYSQL中GET_LOCK的使用

GET_LOCK(str,timeout) 在timeout 秒内尝试获取一个名字为str的锁, 若成功得到锁返回 1,若操作超时还未得到则返回0 (例如,另一个连接获取到了这个锁未释放),若发生错误则返回NULL (诸如缺乏内存或线程mysqladmin kill 被断开 )。 假如你使用GET_LOCK()得到一个锁,当你执行RELEASE_LOCK()时,或你的连接断开(正常或非正常)时,或...

a1007885391的博客 5845

Lock锁及获取锁的四种方法

Lock锁

是小D吖!的博客 2万+

C++标准库中的锁lock_guard、unique_lock、shared_lock、scoped_lock、recursive_mutex

std::mutex.lock是我们在C++中比较常见的锁,我们使用std::mutex.lock方法时,同时需要考虑何时使用std:mutex.unlock方法去解锁。如果在复杂的多线程情况下,加锁、解锁的时机很难把握,也不好实现。RAII原则是。C++新标准提供了lock_guard, 和四种锁,用于各种复杂情况。这四种锁都是满足RAII风格。

weixin_44477424的博客 8676

Java中synchronized与Lock的区别

***//***//***//***//***/}lock():获取锁,如果锁被暂用则一直等待unlock():释放锁tryLock(): 注意返回类型是boolean,如果获取锁的时候锁被占用就返回false,否则返回truetryLock(long time, TimeUnit unit):比起tryLock()就是给了一个时间期限,保证等待参数时间。

Machine Learning with Tutors 1万+

【学习笔记】Java中的锁Lock

本文参考自:Java并发编程:Lock。 Lock锁并不是java内置的功能,其应用场景是在多线程并发访问时,为了避免冲突,需要每个线程先获取锁,避免其他线程的进入,等线程执行完后释放锁,允许其他线程进入。 1. Lock锁与synchronized同步的区别 Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现; synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLo

zuzhiang的博客 3115
上一篇: java中断机制
下一篇: lock学习笔记01
MrMoving
博客等级 码龄10年 54粉丝 145原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值