java线程(四)
java5线程并发库
线程并发库是JDK 1.5版本级以上才有的针对线程并发编程提供的一些常用工具类,这些类被封装在java.concurrent包下。
该包下又有两个子包,分别是atomic和locks两个包。
java.util.concurrent.atomic包
atomic包提供了一些线程相关的工具类,来实现共享数据在多个线程中保持安全访问而不用使用 synchronized关键字进行同步。下面是该报下的一些类。
这里就拿AtomicInteger类来举例,其他类的操作基本上和该类差不多。在JDK的API中说该类可以以原子的方式操作int值,通俗的说就是该类提供了一下对整数类型变量的操作使用该类可以确保在多个线程中访问同一个整数资源时及时不适用锁机制来保持同步也依然能够确保该变数据的安全。下面是给类提供的一些方法。
package cn.wz.traditional.wf; import java.util.concurrent.atomic.AtomicInteger; /**
* Created by WangZhe on 2017/5/8.
*/
public class AtomicTest {
static AtomicInteger data =new AtomicInteger(10);
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
int resoult = data.addAndGet(10);
System.out.println(resoult);
}
}).start();
new Thread(new Runnable() {
public void run() {
int resoult = data.get();
System.out.println(resoult);
}
}).start();
}
}
以addAndGet方法为例的一个demo
那么Atomic包中的类为什么能够实现数据操作的原始性呢?这个我就不得而知了,因为前sun公司并没有把它体现在源码中。我只知道在addAndGet方法中调用get()方法获取变量的原有值,而get方法中返回的value字段被volatile关键字标示,该关键子线程每次获取该变量的值时都会去主内存区获取该变量最新的值,但是这里值得注意的是volatile关键字并不能像synchronized关键字一样确保变量的原子性,至于AtomicInteger是如何确保其原子性的我也不得而知,希望有知道的大神能够分享下,再此先谢过了!下面附上addAndGet方法的源码.
该方法实现被前sun公司隐藏。
Unsafe类的compareAndSwapInt方法(真正的为变量赋值操作)被隐藏
好了其源码就看到这里了其它的类和方法也都差不多,如果有兴趣你可以自己看看源码(然而并没有什么卵用,呵呵),其实我们也不用纠结于其实怎么实现的我们只要知道其可以确保原子性,在需要的是后能够使用就可以了,其和 synchronized同步代码块而言效率和性能是较高的,但其只能适用于一些简单的赋值运算操作,因为我们可以看到其并没有提供乘除以及其它的复杂操作。
java.util.concurrent.locks包
该包提供了一些关于线程锁相关的一些接口和类,我们之前使用synchronized关键字只能设置对象、类、变量等固定的锁,而是用该包则可以时锁变得更加灵活和广泛,该包有三大接口
Condition:Condition
将 Object
监视器方法(wait
、notify
和 notifyAll
)分解成截然不同的对象,以便通过将这些对象与任意 Lock
实现组合使用,为每个对象提供多个等待 set(wait-set)。
Lock:Lock
实现提供了比使用 synchronized
方法和语句可获得的更广泛的锁定操作。
ReadWriteLock:ReadWriteLock 维护了一对相关的锁
,一个用于只读操作,另一个用于写入操作。
Lock 接口方法摘要:
package cn.wz.traditional.wf; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; /**
* Created by WangZhe on 2017/5/8.
*/
public class LockDemo {
static int data=5;
public static void main(String[] args) {
final Lock lock=new ReentrantLock(true);
new Thread(new Runnable() {
public void run() {
lock.lock();
try {
System.out.println("线程1获取锁\t时间:"+System.currentTimeMillis());
data+=5;
System.out.println("线程1休眠1秒钟\t正在等待该所的线程:"+((ReentrantLock)lock).getQueueLength());
System.out.println(data);
Thread.currentThread().sleep(1000);
}catch (Exception e){
lock.unlock();
}finally {
lock.unlock();
};
}
}).start();
new Thread(new Runnable() {
public void run() {
lock.lock();
try {
System.out.println("线程2获取锁\t时间:"+System.currentTimeMillis());
System.out.println(data);
}catch (Exception e){
lock.unlock();
}finally {
lock.unlock();
};
}
}).start();
}
}
LockDemo
从上面的demo中可以看出Lock接口不再指定同步锁定的对象,而是一起本身为锁对象别synchronized更加灵活且没有synchronized关键字必须在同一个代码块儿的限制。上面demo中使用的ReentrantLock类是Lock接口的实现类Lock接口下面还提供了控制读写锁的实现类:ReentrantReadWriteLock.ReadLock和ReentrantReadWriteLock.WriteLock两个类,而这两个类都是ReentrantReaWriteLock类的内部类,如图所示:
而ReentrantReadWriteLock类是ReadWriteLock接口的实现类。这里就在顺便说一下这个接口。该接口只有两个方法定义如下所示:
一个适用于获取ReadLock的锁另一个是用于获取WriteLock的锁这两个锁一般成对出现用来分离数据的读和写进行加锁,比如说一个线程对A资源进行写入操作时另一个线程读取A资源时就需要等待上一个线程释放A资源的写入锁才能获取A资源的读取锁进行数据读取,反之亦然。
package cn.wz.traditional.wf; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock; /**
* Created by WangZhe on 2017/5/8.
*/
public class ReadLockDemo {
static int data=10; public static void main(String[] args) {
final ReentrantReadWriteLock RWlock = new ReentrantReadWriteLock();//获取读写锁实例
final ReentrantReadWriteLock.ReadLock Rlock=RWlock.readLock();//获取读取所实例
final ReentrantReadWriteLock.WriteLock Wlock=RWlock.writeLock();//获取写入锁实例
new Thread(new Runnable() {
public void run() {//线程一
Wlock.lock();
try{
data=5;
System.out.println(data);
System.out.println("线程一写入数据完毕保持写入锁休眠10秒钟");
System.out.println("当前时间:"+System.currentTimeMillis());
Thread.currentThread().sleep(10000);
}catch (Exception e){
Wlock.unlock();
}finally {
Wlock.unlock();
}
}
}).start();
new Thread(new Runnable() {
public void run() {//线程二
Rlock.lock();
try{
System.out.println("线程二获取读取锁,开始进行数据读取");
System.out.println("当前时间:"+System.currentTimeMillis());
System.out.println(data);
}catch (Exception e){
Rlock.unlock();
}finally {
Rlock.unlock();
}
}
}).start();
}
}
读写锁演示demo
Condition接口
Condition接口的出现代替了Object对象的三个改变线程的方法(wait、notify、notifyall)如果说Lock相当于synchronized关键字那Condition就相当于Object监视器的方法。下面是该接口的方法
其中await方法相当于Object的wait方法,Signal方法相当于Object的notify方法,signalAll方法相当于Object对像的notifyAll方法。这里不再详细说明直接通过demo来演示效果。场景如下:
AB两个线程同时操作资源i A线程对i进行加操作,B线程对i进行减操作,当i大于5时A线程等待让B线程进行减操作,反之B线程等待让A线程进行加操作。
package cn.wz.traditional.wf; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; /**
* Created by WangZhe on 2017/5/9.
*/
public class ConditionDemo {
public static void main(String[] args) {
final Lock lock=new ReentrantLock();
final Condition conditionInc=lock.newCondition();;
final Condition conditionDec=lock.newCondition();
final DemoData data=new DemoData();
new Thread(new Runnable() {
public void run() {
lock.lock();
try {
for (int j = 0; j < 50; j++) {
while (data.getI() > 5) {
conditionInc.await();
}
data.inc();
conditionDec.signal();
}
} catch (Exception e) {
lock.unlock();
} finally {
lock.unlock();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
lock.lock();
try{
for(int j=0;j<50;j++) {
while (data.getI() <= 5) {
conditionDec.await();
}
data.dec();
conditionInc.signal();
}
}catch (Exception e){
lock.unlock();
}finally {
lock.unlock();
}
}
}).start();
}
static class DemoData{
private int i; public DemoData(int i) {
this.i = i;
} public DemoData() {
}
public void inc(){
i++;
System.out.println("线程A进行加操作后i的值:"+i);
}
public void dec(){ i--;
System.out.println("线程B进行减操作后i的值:"+i);
}
public int getI() {
return i;
} public void setI(int i) {
this.i = i;
}
}
}
Demo
java线程池的应用
我们知道线程一旦死亡就无法复活(进行重新启动线程。)但是在黑马的面试题中关于java并发的问题中有这样一道面试题:“如何重新启动一个已经死亡的线程?”。刚开始碰到这个问题的人可能会很惊讶,为什么会有这样的面试题呢?我们先来看下面一个demo算不算是重新启动了一个死亡的线程。
package cn.wz.traditional.wf; import com.sun.org.apache.xpath.internal.SourceTree; import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor; /**
* Created by WangZhe on 2017/5/9.
*/
public class ExecutorSignlePool {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new MyRunnable()); }
static class MyRunnable implements Runnable{
public void run() {
for (int i=0;i<10;i++){
if(i==5)
Thread.currentThread().stop();
System.out.println("hello");
}
}
}
}
Demo
从上面例子中可以看到我没在循环到第五次的时候停止当前线程使其死亡,但是我们从结果中可以看到程序并没有结束,那就说明还有线程正在运行,那我们是否可以这样假设当停止当前线程时线程池(executorService)一直试图重新启动线程但重启之后立马又被我们的代码关掉所以一直执行不下去,但也不会结束。那具体是不是这样我也不知道(哈哈),但我知道的是单例线程池(也就是我们上边demo中的executorService)中只能存在一个线程,当期线程死亡后会立马创建一个新的线程, 从一定程度上也可以说是重新启动了一个线程,而且即使线程执行完毕也不会结束除非手动停止线程池
package cn.wz.traditional.wf; import com.sun.org.apache.xpath.internal.SourceTree; import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor; /**
* Created by WangZhe on 2017/5/9.
*/
public class ExecutorSignlePool {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
MyRunnable myRunnable = new MyRunnable();
executorService.execute(myRunnable);
executorService.execute(myRunnable); }
static class MyRunnable implements Runnable{
public void run() {
System.out.println("开始执行");
System.out.println("hello");
}
}
}
demo
除了单线程池意外我们开可以创建固定大小的线程池(newFixedThreadPool方法创建)和缓存线程池(newCachedThreadPool方法创建)这两个方法都返回ExecutorService类型对象。
固定大小的线程池:该池中的线程个数是固定的当线程任务(Runnable对象)个数大于设置的线程个数时超出的任务等待之前任务执行后再执行。
缓存线程池:该池初始没有线程,当有任务进来时创建线程并在执行结束后缓存一段时间。在改时间之内有新的任务进来则不再创建新线程直接使用原有线程。
java线程(四)的更多相关文章
- java线程四种状态
一个线程可以有四种状态: 1.新(new), 即线程刚刚创建,而并未执行 2.可运行(runnable),意味着一旦时间分片机制有空闲的CPU周期提供给一个线程,那个线程便可立即开始运行.因此,线程可 ...
- Java线程专栏文章汇总(转)
原文:http://blog.csdn.net/ghsau/article/details/17609747 JDK5.0之前传统线程 Java线程(一):线程安全与不安全 Java线程 ...
- Java线程专栏文章汇总
转载自 http://blog.csdn.net/ghsau/article/details/17609747 JDK5.0之前传统线程 Java线程(一):线程安全与不安全 J ...
- 四种Java线程池用法解析
本文为大家分析四种Java线程池用法,供大家参考,具体内容如下 http://www.jb51.net/article/81843.htm 1.new Thread的弊端 执行一个异步任务你还只是如下 ...
- Java进阶(四十三)线程与进程的区别
Java进阶(四十三)线程与进程的区别 1.线程的基本概念 概念:线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必 ...
- Java并发编程:Java的四种线程池的使用,以及自定义线程工厂
目录 引言 四种线程池 newCachedThreadPool:可缓存的线程池 newFixedThreadPool:定长线程池 newSingleThreadExecutor:单线程线程池 newS ...
- Java ExecutorService四种线程池的例子与说明
1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? new Thread(new Runnable() { @Override public void run() { ...
- 面试题:四种Java线程池用法解析 !=!=未看
1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? 1 2 3 4 5 6 7 8 new Thread(new Runnable() { @Override ...
- Java ExecutorService四种线程池的例子与说明(转发)
1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? new Thread(new Runnable() { @Override public void run() { ...
- Java ExecutorService四种线程池及自定义ThreadPoolExecutor机制
一.Java 线程池 Java通过Executors提供四种线程池,分别为:1.newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收 ...
随机推荐
- js代码实现放大镜效果
每当打开淘宝,天猫等pc端时,看到心仪的物品时,点击图片时,便呈现出放大的效果.在没有去理解分析它的原理时,感觉非常的神奇,当真正地去接触,也是非常好理解.如下图展示所见: 很是常见,在此记载一下,毕 ...
- linux服务器证书安装指引
下面提供了3类服务器证书安装方法的示例: 1. Apache 2.x 证书部署 1.1 获取证书 Apache文件夹内获得证书文件 1_root_bundle.crt,2_www.domain.com ...
- waypoints
http://imakewebthings.com/waypoints waypoints 滑冰122分钟 Cygwin http:/nxutils.sourceforge.net http://ba ...
- 深入浅出分析MySQL MyISAM与INNODB索引原理、优缺点、主程面试常问问题详解
本文浅显的分析了MySQL索引的原理及针对主程面试的一些问题,对各种资料进行了分析总结,分享给大家,希望祝大家早上走上属于自己的"成金之路". 学习知识最好的方式是带着问题去研究所 ...
- 模块化规范Common.js,AMD,CMD
随着网站规模的不断扩大,嵌入网页中的javascript代码越来越大,开发过程中存在大量问题,如:协同开发,代码复用,大量文件引入,命名冲突,文件依赖. 模块化编程称为迫切的需求. 所谓的模块,就是实 ...
- SQL Server-基础-经典SQL语句
经典SQL语句 基础 .说明:创建数据库 CREATE DATABASE database-name .说明:删除数据库 drop database dbname .说明:备份sql server - ...
- 5w2h分析法则
5W2H分析法 5W2H分析法又叫七何分析法,是二战中美国陆军兵器修理部首创.简单.方便,易于理解.使用,富有启发意义,广泛用于企业管理和技术活动,对于决策和执行性的活动措施也非常有帮助,也有助于弥补 ...
- GPIO寄存器
GPIO寄存器描述 <STM32参考手册中文-p75> 1.端口配置低寄存器(GPIOx_CRL)(x = A...E)2.端口配置高寄存器(GPIOx_CRH)(x = A...E) 3 ...
- css中auto的用法
—什么是auto? +auto是自适应的意思,auto是很多尺寸值的默认值,也就是由浏览器自动计算. +块级元素中margin.border.padding以及content宽度之和构成父元素widt ...
- phpcms 笔记
首先是要把首页分为三个部分 : 导航部分 .尾部和首页中间部分 用了三个不同的文件 header.html ; index.html; footer.html 在使用phpcms之前 首先 ...