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:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收 ...
随机推荐
- MyBatis原始dao开发及问题总结(五)
一.MyBatis原始Dao开发方式 1.原始dao开发需要程序员编写dao接口和dao接口实现类 编写UserDao接口:UserDao.java package codeRose.dao; pub ...
- mac压缩文件乱码
http://blog.b3inside.com/apple/solve-file-name-garbled-with-betterzip/
- RDCの自我介绍
........................................... 大家好,这里是RUSH_D_CAT.一只ACMer,19岁的少年,From SDU,大二. Q: 1950499 ...
- Spring execution表达式
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) th ...
- Luogu1486郁闷的出纳员【Splay】
P1486 郁闷的出纳员 题目描述 OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工作,但是令人郁闷的是,我们的老板反 ...
- es suggest did you mean资料
term suggester 功能介绍 term suggester 根据提供的文档提供搜索关键词的建议,也就是关键词自动纠错.该链接介绍如何使用 term suggester 语法.term sug ...
- 使用cocapods报错 [!] Your Podfile has had smart quotes sanitised. To avoid issues in the future, you should not use TextEdit for editing it. If you are not using TextEdit, you should turn off smart quotes
从github上下载的工程大部分都使用了cocapods,在install的时候可能会报错. 报错原因: 1.不要使用文本编辑去编辑Podfile文件,使用Xcode编辑,或者使用终端敲命令去编辑. ...
- php 1到100累加 新方法
<?php $sum = 0; for($i=0;$i<=100;$i++){ $sum += $i; } echo $sum; 之前只是这么写. 现在发现可以这么写 $sum = arr ...
- poj 3270 Cow Sorting (置换入门)
题意:给你一个无序数列,让你两两交换将其排成一个非递减的序列,每次交换的花费交换的两个数之和,问你最小的花费 思路:首先了解一下什么是置换,置换即定义S = {1,...,n}到其自身的一个双射函数f ...
- Spring基础学习(二)—详解Bean(上)
在Spring配置文件中,用户不但可以将String.int等字面值注入Bean中,还可以将集合.Map等类型注入Bean中,此外还可以注入配置文件中其他定义的Bean. 一.字面值 ...