Java多线程--JDK并发包(1)
Java多线程--JDK并发包(1)
之前介绍了synchronized关键字,它决定了额一个线程是否可以进入临界区;还有Object类的wait()和notify()方法,起到线程等待和唤醒作用。synchronized关键字经常和它们一起使用,因为wait和notify在调用之前需要获得“锁”,而锁时依靠synchronized获得的。
同步机制
重入锁
下面是一个使用ReentrantLock的简单例子。可以看到,和synchronized相比,必须手动调用unlock()方法释放资源。
import java.util.concurrent.locks.ReentrantLock;
public class ReEntryLock {
public static class Demo implements Runnable {
public static ReentrantLock lock = new ReentrantLock();
public static int i = 0;
@Override
public void run() {
for (int j = 0; j < 1000; j++) {
lock.lock();
try {
i++;
} finally {
lock.unlock();
}
}
}
}
}
顾名思义,重入就是一个线程可以反复进入同一把锁,注意仅限于一个线程。下面的例子演示了一个个线程两次获得一把锁。
lock.lock();
lock.lock();
try{
i++;
} finally {
lock.unlock();
lock.unlock();
}
有一点要注意,获得几次锁,释放时也放释放对应的次数,不能多释放,否则会抛出异常;少释放的话相当于该线程还持有着锁,其他线程依然不能进入临界区。
中断响应
对于synchronized,若一个线程在等待锁,要么:
- 拿到锁,继续执行;
- 继续等待。
而重入锁可以响应中断,lockInterruptibly()是一个可以对中断进行响应的锁申请动作。当线程调用interrupt()方法后,正在等待锁的线程可以接收到这个通知,被告知不用再等待,可以停止了。线程对中断的响应对处理死锁有一定帮助。
有等待时限的锁申请
在申请锁的时候使用tryLock(5, TimeUnit.SECONDS)就表示该线程最多等待5s,5s后还得不到就会返回false表示放弃对锁的等待。tryLock()还可以没有参数,此时如果锁没有被其他线程占用就立即返回true, 否则立即返回false,不会等待,可以认为是传入了时间0s。使用该方法可以改善死锁和饥饿的情况。
公平锁
所谓公平锁,即按照线程进入等待队列时间的先后顺序,先来的可以先得到锁。公平锁不会产生饥饿,只要排队,总有个时候轮到你。
synchronized进行锁控制产生的锁是不公平的,ReentrantLock有个构造函数,可以传入一个boolean类型的值,设定为true就能产生公平锁。
Condition条件
wait()和notify()\notifyAll()与synchronized关键字搭配使用,condition有await()和signal()\singalAll()方法,与重入锁搭配使用。和wait()\notify()一样,await()\signal()也需要在调用之前获得重入锁,await()被调用后会释放重入锁。
Condition是由lock.newCondition()生成的,可见这条语句生成一个和当前重入锁绑定的Condition。
// a Runnale class
@Override
public void run() {
try {
lock.lock();
// 当前线程在condition对象上等待(进入condition的等待队列)
condition.await();
} finally {
lock.unlock();
}
}
// in main
t1.start();
Thread.sleep(2000);
lock.lock();
// main主线程唤醒在condition上等待的t1线程(condition从队列释放一个线程)
condition.signal();
lock.unlock();
信号量
不论是synchronized还是重入锁ReentrantLock,都是一次只能允许一个线程访问一个资源。使用信号量Semaphore可以指定多个线程,同时访问某一个资源。
public Semaphore(int permits);
public Semaphore(int permits, boolean fair);
permits参数就决定了信号量能同时申请多少个许可,也就是指定允许多少个线程同时访问一个资源,fair表示是否为公平锁。
信号量使用起来和lock很像,不同的是允许指定个数的线程进入。举个例子:
Semaphore s = new Semaphore(5); // 允许同时5个线程进入临界区
@Override
public void run() {
s.acquire(); // 有点类似lock.lock();
System.out.println(Thread.currentThread().getId()); // 和lock不同,这个地方可允许5个线程同时进来
s.release(); // 有点类似lock.unlock();
}
其中acquire()尝试获得一个准入的许可,若无法获得会等待;release()则是访问资源结束后释放许可,使得其他线程可以得到这个许可。
ReadWriteLock读写锁
读数据并不会对数据造成损坏,所以应允许多个线程同时读取,当时当有线程参与写操作时,应该阻塞——也就是线程之间:
- 读-读不互斥,不阻塞;
- 读-写互斥,读会阻塞写,写也会阻塞读;
- 写-写互斥,写写相互阻塞。
读写锁在读取次数远大于写次数时,有很好的性能。
倒计时类CountDownLatch
public CountDownLatch(int count)
接受一个参数count,也就是计数个数。
public static final CountDownLatch cdl = new CountDowmLatch(10); // 计数个数10
public static final CountDownLatchDemo demo = new CountDowmLatchDemo();
// class CountDowmLatchDemo implements Runnable
@Override
public void run() {
try {
/* some task */
Thread.sleep(1000);
cdl.countDown(); // 一个任务已完成,计数器减1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// in main
ExecutorService exec = Executors.newFixedThreadPool(10); // 固定10个线程的线程池
for (int i = 0; i < 10; i++) {
exec.submit(demo);// 提交10个任务,也就是开了10个线程
}
// 当前的main线程阻塞在了cdl上,检查任务全部任务都完成后,在这里就是等cdl计数10次完毕后才可以继续执行
cdl.await();
System.out.println("计时10s终于结束了,该我表演了");
exec.shutDown(); // 别忘了关闭线程池,否则程序不会终止
循环栅栏CyclicBarrier
这个类也可以完成计数功能,但是比CountDownLatch更强大。这个计数器是可以循环使用的,比如设定了计数10,凑齐了第一批10个线程后,又开始一轮新的计数......
CyclicBarrier的强大之处在于在构造函数中还可以指定一个实现了Runnable接口的实例,表示每结束一次计数,就执行一次动作。结合循环可以实现强大的功能。比如每十分钟去检查一遍下载进度条...
线程阻塞工具LockSupport
该类可以在线程内任意位置让线程阻塞,不像Thread.suspend()那样由于在suspend之前就调用resume而导致的线程永久挂起,也不像wait()方法一样必须获得对象的锁才能调用。主要是park()和unpark()方法,即使unpark()在park()之前被调用了,也不用担心线程被永久挂起。因为LockSupport使用了类似信号量的机制,它为每个线程都准备了一个许可,若许可可用,park()立即返回,并将许可变成不可用;如果许可不可用,就会阻塞。而unpark()将一个许可变成可用。所以即使unpark()在park()之前被调用了,park()方法就立即返回了,这就杜绝了永久挂起的现象。
park()还能响应中断,且不抛出异常,只是默默返回。
by @sunhaiyu
2018.4.23
Java多线程--JDK并发包(1)的更多相关文章
- Java多线程--JDK并发包(2)
Java多线程--JDK并发包(2) 线程池 在使用线程池后,创建线程变成了从线程池里获得空闲线程,关闭线程变成了将线程归坏给线程池. JDK有一套Executor框架,大概包括Executor.Ex ...
- java多线程:并发包中ConcurrentHashMap和jdk的HashMap的对比
一:HashMap--->底层存储的是Entry<K,V>[]数组--->Entry<K,V>的结构是一个单向的链表static class Entry<K, ...
- Java多线程之并发包,并发队列
目录 1 并发包 1.1同步容器类 1.1.1Vector与ArrayList区别 1.1.2HasTable与HasMap 1.1.3 synchronizedMap 1.1.4 Concurren ...
- java多线程:并发包中的信号量和计数栓的编程模型
一:信号量的编程模型 package com.yeepay.sxf.test.atomic.test; import java.util.concurrent.Semaphore; /** * 测试信 ...
- java多线程:并发包中ReentrantReadWriteLock读写锁的锁降级模板
写锁降级为读锁,但读锁不可升级或降级为写锁. 锁降级是为了让当前线程感知到数据的变化. //读写锁 private ReentrantReadWriteLock lock=new ReentrantR ...
- java多线程:并发包中ReentrantReadWriteLock读写锁的原理
一:读写锁解决的场景问题--->数据的读取频率远远大于写的频率的场景,就可以使用读写锁.二:读写锁的结构--->用state一个变量.将其转化成二进制,前16位为高位,标记读线程获取锁的次 ...
- java多线程:并发包中ReentrantLock锁的公平锁原理
一:锁的原理结构 (1)锁对象内部维护了一个同步管理器的对象AbstractQueuedSynchronizer,AbstractOwnableSynchronizer (2)该对象其实是一个抽象类, ...
- 《java多线程编程核心技术》(一)使用多线程
了解多线程 进程和多线程的概念和线程的优点: 提及多线程技术,不得不提及"进程"这个概念.百度百科对"进程"的解释如下: 进程(Process)是计算机中的程序 ...
- Java多线程编程核心技术(一)Java多线程技能
1.进程和线程 一个程序就是一个进程,而一个程序中的多个任务则被称为线程. 进程是表示资源分配的基本单位,线程是进程中执行运算的最小单位,亦是调度运行的基本单位. 举个例子: 打开你的计算机上的任务管 ...
随机推荐
- GridControl简单属性操作
1.单行记录整行选中 GridView->OptionsBehavior->EditorShowMode 设置为:Click 2.如何让行只能选择而不能编辑(或编辑某一单元格) 只读 Gr ...
- Maximum repetition substring(POJ - 3693)(sa(后缀数组)+st表)
The repetition number of a string is defined as the maximum number \(R\) such that the string can be ...
- [学习笔记]BSGS
\(\%\%\% Fading\) 早就会了,我最近才理解,当时颓废太多忘学了 1.[SDOI2013]随机数生成器 当天正好在学数列,回来发现用必修五的知识就没了-- 不过特判好烦啊. \(Code ...
- jQuery基础(4)- 位置信息、事件流、事件对象、事件代理、jquery事件
一.jQuery的位置信息 jQuery的位置信是JS的client系列.offset系列.scroll系列封装好的一些简便api. 1.宽度和高度 a.获取宽度和高度,例如: .width() // ...
- 大脸猫讲逆向之ARM汇编中PC寄存器详解
i春秋作家:v4ever 近日,在研究一些开源native层hook方案的实现方式,并据此对ARM汇编层中容易出问题的一些地方做了整理,以便后来人能有从中有所收获并应用于现实问题中.当然,文中许多介绍 ...
- webApp在各大Android市场上的发布
本来打算每个月都写上一篇博客的,可是计划永远赶不上变化,不过这其中也有自己的懒惰,果然过年让整个人懈怠了不少.年后一直在赶项目以致于到今天才动手写这篇文章. 这一篇主要写点在公司的要求下发布的webA ...
- ping端口是否开放(windows,macos,linux)
windows中ping端口:tcping命令 1. tcping 非自带命令,首先安装tcping命令,也可以去官网:http://www.elifulkerson.com/projects/tcp ...
- 使用命令行工具npm新创建一个vue项目
使用vue开发项目的前期工作可以参考前面写的: Vue环境搭建及node安装过程整理 Vue.js 提供一个官方命令行工具,可用于快速搭建大型单页应用.该工具提供开箱即用的构建工具配置,带来现代化的 ...
- python安装mysqlclient模块报fatal error: Python.h:解决方法
在搭建Flask框架安装mysqlclient模块时候老是报fatal error: Python.h:错误,折腾老半天,百度了老半天看了不少大神帖子,就是没解决, 后来发现这不是个BUG,都是自己的 ...
- sql 导入导出表数据 命令
那么在我们使用BCP命令之前,我们首先要在Sql Server数据库中执行下列语句,以修改Sql Server的配置,启用对BCP命令的支持. --允许配置高级选项 exec sp_configure ...