《JAVA并发编程实战》显式锁

Lock和ReentrantLock

public interface Lock{
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long timeout,TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

Lock提供的是一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁的方法都是显式的。

使用Lock时,必须在finally块中释放锁。否则,在被保护的代码中抛出了异常,那么这个锁永远都无法释放。

Lock Lock = new ReentrantLock();
lock.lock();
try{
    
} finally {
    lock.unlock();
}

轮询锁和定时锁

可定时的和可轮询的锁获取模式是由tryLock方法实现的。在内置锁中,死锁是一个严重的问题,恢复程序的唯一方法是重新启动程序,而防止死锁的唯一方法就是在构造程序时避免出现不一致的锁顺序。可定时的和可轮询的锁提供了另外一种选择:避免死锁的发生。

如果不能获得所有需要的锁,那么可以使用可定时的或可轮询的锁获取方式,从而使你重新获得控制权,它会释放已经获得的锁,然后重新尝试获取所有锁(或者至少会将这个失败记录到日志,并采取其他措施)。

public boolean transferMoney(Account from,Account to,DollarAmount amount,long timeout,TimeUnit unit) throws InsufficientFundsException,InterruptedException {
    long fixedDelay = getFixedDelayComponentNanos(timeout,unit);
    long randMod = getRandomDelayComponentNanos(timeout,unit);
    long stopTime = System.nanoTime() + unit.toNanos(timeout);
    
    while(true){
        if(from.lock.tryLock()){
            try{
               if(to.lock.tryLock()){
                    try{
                        if(from.getBalance().compareTo(amount) < 0){
                            throw new InsufficientFundsException();
                        }else{
                            from.debit(amount);
                            to.credit(amount);
                            return true;
                        }
                    } finally {
                        to.lock.unlock();
                    } 
               } finally {
                   from.lock.unlock();
               }
        }
        if(System.nanoTime < stopTime){
            return false;
        }
        NANOSECONDS.sleep(fixedDelay + rnd.nextLong() % randMod);
    }
}

带时间限制的加锁

public boolean trySendOnsharedLine(String message,long timeout,TimeUnit unit) throws InterruptedException {
    long nanosToLock = unit.toNanos(timeout) - estimatedNanosToSend(message);
    
    if(!lock.tryLock(nanosToLock,NANOSECONDS)){
        return false;
    }
    try{
        return sendOnSharedLine(message);
    } finally {
        lock.unlock();
    }
}

PS:不先加锁直接解锁:

public class ReentrantLock_learning {
    private static Lock lock = new ReentrantLock();

    public static void main(String[] args){
        try {

        } finally {
            lock.unlock();
        }
    }
}
java.lang.IllegalMonitorStateException

可中断的锁获取操作

public boolean sendOnSharedLine(String message) throws InterruptedException{
    lock.lockInterruptibly();
    try{
        return cancellableSendOnSharedLine(message);
    } finally {
        lock.unlock();
    }
}

private boolean cancellableSendOnSharedLine(String message) throws InterruptedException{
    ...
}

非块结构的加锁

在内置锁中,锁的获取和释放都是基于代码块的。而降低锁的粒度能提高代码的可伸缩性。

性能考虑因素

java5.0中,从单线程到多线程内置锁的性能将急剧下降,但在java6.0中对内置锁进行了优化,内置锁的性能不会由于竞争而急剧下降。

公平性

在ReentrantLock的构造函数中提供了两种公平性选择:创建一个非公平的锁或公平的锁。在公平锁上,线程将按照他们发出请求的顺序来获得锁,但在非公平的锁上,允许“插队”。

在激烈竞争的情况下,非公平锁额性能高于公平锁的性能的一个原因是:在恢复一个被挂起的线程和该线程真正开始运行之间存在着严重的延迟。

在synchronized和ReentrantLock中选择

在一些内置锁无法满足需求的情况下,ReentrantLock可以作为一种高级工具。当需要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时的、可轮询的、可中断的锁获取操作,公平队列,以及非块结构的锁。

ReentrantLock的非块结构特性仍然意味着获取锁的操作不能和特定的栈帧关联起来,而内置锁却可以。在线程转储时有一定区别。

读-写锁

ReentrantLock实现了一种标准的互斥锁:每次最多只有一个线程能持有ReentrantLock。但对于维护数据的完整性,互斥通常是一种过于强硬的加锁规则,因此也就不必要的限制了并发性。

读/写锁:一个资源可以被多个读操作访问,或者被一个写操作访问,但两者不能同时进行。

public interface ReadWriteLock{
    Lock readLock();
    Lock writeLock();
}

在读取锁和写入锁之间的交互可以采用多种实现方式。ReadWriteLock中的一些可选实现包括:

  1. 释放优先。当一个写入操作释放写入锁时,并且队列中同时存在读线程和写线程,那么应该优先选择读线程,写线程还是最先请求的线程?
  2. 读线程插队。如果锁是由读线程持有,但有写线程等待,那么新到达的读线程能否立即获得访问权,还是应该在写线程后等待?如果允许读线程插队到写线程之前,那么将提高并发性,但却可能造成写线程饥饿的问题。
  3. 重入性。读锁和写锁能否重入?
  4. 降级。如果一个线程持有写入锁,那么它能否在不释放该锁的情况下获得读锁?这可能会使写入锁被降级为读取锁,同时不允许其他写线程修改被保护的资源。
  5. 升级。读锁能否优先于其他正在等待的读线程和写线程而升级为一个写入锁?在大多数的读-写锁实现中并不支持升级,因为如果没有显式的升级操作,那么很容易造成死锁。(如果两个读线程同时升级为写入锁,那么两者都不会释放读锁)

ReentrantReadWriteLock在构造时也可以选择是否为公平锁。在公平锁中,等待时间最长的线程将优先获取锁。如果这个锁由读线程持有,而另一个线程请求写入锁,那么其他读线程都不能获得读取锁,直到写线程使用完并且释放了写入锁。在非公平的锁中,线程获得访问许可的顺序是不确定的。写线程降级为读线程是可以的,读线程升级写线程是不可以的。

public class ReadWriteMap<K,V>{
    private final Map<K,V> map;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock r = lock.readLock();
    private final Lock w = lock.writeLock();
    
    public ReadWriteMap(Map<K,V> map){
        this.map = map;
    }
    
    public V put(K key,V value){
        w.lock();
        try{
            return map.put(key,value);
        } finally {
            w.unlock();
        }
    }
    
    public V get(Object key){
        r.lock();
        try{
            return map.get(key);
        } finally {
            r.unlock();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
机器学习是一种人工智能(AI)的子领域,致力于研究如何利用数据和算法让计算机系统具备学习能力,从而能够自动地完成特定任务或者改进自身性能。机器学习的核心思想是让计算机系统通过学习数据中的模式和规律来实现目标,而不需要显式地编程。 机器学习应用非常广泛,包括但不限于以下领域: 图像识别和计算机视觉: 机器学习在图像识别、目标检测、人脸识别、图像分割等方面有着广泛的应用。例如,通过深度学习技术,可以训练神经网络来识别图像中的对象、人脸或者场景,用于智能监控、自动驾驶、医学影像分析等领域。 自然语言处理: 机器学习在自然语言处理领域有着重要的应用,包括文本分类、情感分析、机器翻译、语音识别等。例如,通过深度学习模型,可以训练神经网络来理解和生成自然语言,用于智能客服、智能助手、机器翻译等场景。 推荐系统: 推荐系统利用机器学习算法分析用户的行为和偏好,为用户推荐个性化的产品或服务。例如,电商网站可以利用机器学习算法分析用户的购买历史和浏览行为,向用户推荐感兴趣的商品。 预测和预测分析: 机器学习可以用于预测未来事件的发生概率或者趋势。例如,金融领域可以利用机器学习算法进行股票价格预测、信用评分、欺诈检测等。 医疗诊断和生物信息学: 机器学习在医疗诊断、药物研发、基因组学等领域有着重要的应用。例如,可以利用机器学习算法分析医学影像数据进行疾病诊断,或者利用机器学习算法分析基因数据进行疾病风险预测。 智能交通和物联网: 机器学习可以应用于智能交通系统、智能城市管理和物联网等领域。例如,可以利用机器学习算法分析交通数据优化交通流量,或者利用机器学习算法分析传感器数据监测设备状态。 以上仅是机器学习应用的一部分,随着机器学习技术的不断发展和应用场景的不断拓展,机器学习在各个领域都有着重要的应用价值,并且正在改变我们的生活和工作方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值