Java并发初探:
(1)注意复合性操作带来的数据修改的异常,对于一般的计数器之类的变量,可以采用位于
java.util.concurrent.atomic包中的原子性对象
(2)变量的可见性:当有多个线程对一个变量进行读写操作时,其中一个线程对变量的更改可能会因为寄存器缓存等原因使得其他线程无法得到更新的数据,可以使用volatile关键字来声明变量
(3)利用线程封闭来实现线程安全:ThreadLocal类的使用,其实质是建立了一个Thread和value的map映射
(5)使用synchronized来进行线程同步
(6)加锁时应注意对于同一个对象所加锁的一致性,否则同样不是线程安全的
(7)同步容器类:Vector、HashTable(其迭代器是快速失败的)、Collections.synchronized...()构建同步集合
(该类还包含很多Collection的实用方法)
(8)并发容器类:同步容器类中对于状态的访问都是串行化的,并发容器类增强了并发的性能,位于java.util.concurrent包下;
8.1:ConcurrentMap:提供其他原子 putIfAbsent、remove、replace 方法的 Map
8.2:CopyOnWriteArrayList:ArrayList 的一个线程安全的变体,其中所有可变操作(add、set 等等)都是通过对底层数组进行一次新的复制来实现的,当遍历远远大于修改时比较适用
8.3:...BlockingQueue(三种实现):阻塞队列,适用于生产者-消费者模型,注意其提供的一些方法对于能够立即处理的做法,与之类似的还有...BlockingDeque
8.4:SynchronousQueue:一种阻塞队列,其中每个插入操作必须等待另一个线程的对应移除操作 ,反之亦然,同步队列没有任何内部容量,甚至连一个队列的容量都没有,适合于传递性设计
(9)同步工具类:
9.1:闭锁:相当于一扇门,在指定条件未达到之前,不允许任何线程通过,一旦允许便无法改变状态一直保持打开状态,CountDownLatch是一个闭锁的实现,个人认为对于需要一个准备完成的标记工作的任务比较适用
9.2:FutureTask:可取消的异步计算。利用开始和取消计算的方法、查询计算是否完成的方法和获取计算结果的方法,提供了对 Future 的基本实现(同时也实现了runnable接口)。仅在计算完成时才能获取结果;如果计算尚未完成,则阻塞 get 方法,一旦计算完成,就不能再重新开始或取消计算
9.4:Semaphore:一个计数信号量,维护了一个许可集,在许可可用前会阻塞每一个
(1)注意复合性操作带来的数据修改的异常,对于一般的计数器之类的变量,可以采用位于
java.util.concurrent.atomic包中的原子性对象
(2)变量的可见性:当有多个线程对一个变量进行读写操作时,其中一个线程对变量的更改可能会因为寄存器缓存等原因使得其他线程无法得到更新的数据,可以使用volatile关键字来声明变量
(3)利用线程封闭来实现线程安全:ThreadLocal类的使用,其实质是建立了一个Thread和value的map映射
使得对于每一个线程拥有一个独立的数据备份,而不相互影响,应用:对于数据库的连接对象等
ThreadLocal例子:
public class learn {
public static void main(String []args){
Set<Thread> hashSet=new HashSet<Thread>();
ThreadLocalVar localVar=new ThreadLocalVar();
for(int i=0;i<100;i++){
ChangeThread thread=new ChangeThread(localVar,i);
thread.start();
hashSet.add(thread);
}
while(true){
int i=0;
for (Thread thread : hashSet) {
if(thread.isAlive()){
break;
}
i++;
}
if(i>=100)
break;
}
System.out.println("value is"+localVar.get());//由结果可以知道虽然每个线程都改动了number,但是原始数据并不会被改动
}
}
class ChangeThread extends Thread{
ThreadLocalVar localVar;
int index=0;
public ChangeThread(ThreadLocalVar localVar,int index) {
this.localVar = localVar;
this.index=index;
}
@Override
public void run() {
int y=localVar.get();//只是得到一个数据的拷贝
System.out.println("index="+y);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
localVar.set(y+index);//对原始数据并不存在影响
System.out.println("change index="+localVar.get());
}
}
class ThreadLocalVar{
int number=0;
private final ThreadLocal<Integer> numberlocal=new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return number;
}
};
public int get() {
return numberlocal.get();
}
public void set(int value){
numberlocal.set(value);
}
}
(4)关注可变变量的线程安全,使用final对象是最简单的解决方法
(5)使用synchronized来进行线程同步
(6)加锁时应注意对于同一个对象所加锁的一致性,否则同样不是线程安全的
(7)同步容器类:Vector、HashTable(其迭代器是快速失败的)、Collections.synchronized...()构建同步集合
(该类还包含很多Collection的实用方法)
(8)并发容器类:同步容器类中对于状态的访问都是串行化的,并发容器类增强了并发的性能,位于java.util.concurrent包下;
8.1:ConcurrentMap:提供其他原子 putIfAbsent、remove、replace 方法的 Map
8.2:CopyOnWriteArrayList:ArrayList 的一个线程安全的变体,其中所有可变操作(add、set 等等)都是通过对底层数组进行一次新的复制来实现的,当遍历远远大于修改时比较适用
8.3:...BlockingQueue(三种实现):阻塞队列,适用于生产者-消费者模型,注意其提供的一些方法对于能够立即处理的做法,与之类似的还有...BlockingDeque
8.4:SynchronousQueue:一种阻塞队列,其中每个插入操作必须等待另一个线程的对应移除操作 ,反之亦然,同步队列没有任何内部容量,甚至连一个队列的容量都没有,适合于传递性设计
(9)同步工具类:
9.1:闭锁:相当于一扇门,在指定条件未达到之前,不允许任何线程通过,一旦允许便无法改变状态一直保持打开状态,CountDownLatch是一个闭锁的实现,个人认为对于需要一个准备完成的标记工作的任务比较适用
CountDownLatch使用:
class TestCountDownLatch{
public static int number=10;
private CountDownLatch startLatch=new CountDownLatch(1);
private CountDownLatch endLatch=new CountDownLatch(number);
public void CalcuteTime() {
for(int i=0;i<number;i++){
Thread thread=new Thread(){
@Override
public void run() {
try {
startLatch.await();//在startLatch降为0之前一直阻塞,导致后面的方法无法调用
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
finally{
endLatch.countDown();//为endLatch脱离阻塞
}
}
};
thread.start();
}
long startime=System.nanoTime();
startLatch.countDown();
try {
endLatch.await();//在endLatch变为0之前一直阻塞
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long endtime=System.nanoTime();
System.out.println("use time="+(endtime-startime));
}
}
9.2:FutureTask:可取消的异步计算。利用开始和取消计算的方法、查询计算是否完成的方法和获取计算结果的方法,提供了对 Future 的基本实现(同时也实现了runnable接口)。仅在计算完成时才能获取结果;如果计算尚未完成,则阻塞 get 方法,一旦计算完成,就不能再重新开始或取消计算
9.3:Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的,都需要利用Thread来进行执行
Callable->FutureTask->Thread 或者 Excutor.submit(callable)->future;
FutureTask使用,也可以使用Executor来提交任务:
public class learn implements Callable<Integer> {
public static void main(String []args){
long starttime=System.currentTimeMillis();
FutureTask<Integer> task=new TestFutureTask(new learn());
Thread thread=new Thread(task);
thread.start();
try {
System.out.println("answer="+task.get());
long endtime=System.currentTimeMillis();
System.out.println("wait time ="+(endtime-starttime));
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
@Override
public Integer call() throws Exception {
int sum=0;
for(int i=0;i<300;i++){
Thread.sleep(100);
sum+=i;
}
return sum;
}
}
class TestFutureTask extends FutureTask<Integer> {
public TestFutureTask(Callable<Integer> arg0) {
super(arg0);
}
}
9.4:Semaphore:一个计数信号量,维护了一个许可集,在许可可用前会阻塞每一个
acquire()
,然后再获取该许可,将信号量初始化为 1,使得它在使用时最多只有一个可用的许可,从而可用作一个相互排斥的锁,其构造方法可选地接受一个
公平 参数,觉得有阻塞队列的意思,通常用于限制可以访问某些资源(物理或逻辑的)的线程数目
Semaphore使用:
public class learn {
public static void main(String []args){
new TestSemaphore().start();
new Thread(new Runnable() {
public synchronized void test(int num){//可重入
if(num==0)
return;
System.out.println("ready "+num);
test(num-1);
System.out.println("hello world "+num);
}
@Override
public void run() {
test(10);
}
}).start();
}
}
class TestSemaphore extends Thread{
Semaphore semaphore;//与线程无关,当前获得允许的线程并不能重入
public TestSemaphore() {
semaphore=new Semaphore(1);
}
@Override
public void run() {
getAcquire();
}
public void getAcquire(){
System.out.println("avaliable number="+semaphore.availablePermits());
try {
semaphore.tryAcquire(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("exception avaliable number="+semaphore.availablePermits());
}
getAcquire();
}
}
9.5栅栏类似于闭锁,同样能阻塞一组线程知道某个事件发生,当所有的参与者都调用了await后才继续执行,如果当前线程是最后一个将要到达的线程(await),并且构造方法中提供了一个非空的屏障操作,则在允许其他线程继续运行之前,当前线程将运行该操作。如果在执行屏障操作过程中发生异常,则该异常将传播到当前线程中,并将 barrier 置于损坏状态,另外个人觉得二者的区别不大,但是存在细微的区别。
CyclicBarrier是一种实现类,使用如下
public class learn {
public static void main(String []args){
testCyclicBarrier();
}
public static void testCyclicBarrier(){
ExecutorService executor=Executors.newFixedThreadPool(3);//参数为参与者两个RunSomething线程和一个主线程
CyclicBarrier barrier=new CyclicBarrier(3, new Runnable() {
@Override//在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次
public void run() {
System.out.println("all thread have already finished");
}
});
class Runsomething implements Runnable{
CyclicBarrier bar;
public Runsomething(CyclicBarrier bar) {
super();
this.bar = bar;
}
@Override
public void run() {
System.out.println("a thread ready");
try {
bar.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("a thread finished");
}
}
executor.execute(new Runsomething(barrier));
executor.execute(new Runsomething(barrier));
try {
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
}
System.out.println("function ended!");
}
}
Exchanger是另一种实现,每个线程将条目上的某个方法呈现给 exchange 方法,与伙伴线程进行匹配,并且在返回时接收其伙伴的对象。Exchanger 可能被视为 SynchronousQueue 的双向形式,用于两个线程之间交换数据,当二者都达到临界点时进行交换,否则会阻塞
Echanger使用,由打印结果可知基本维持了线程的同步
public class learn {
public static void main(String []args){
Exchanger<Integer> exchanger = new Exchanger<>();
new Consumer(exchanger).start();
new Producer(exchanger).start();
}
}
class Producer extends Thread {
Exchanger<Integer> exchanger = null;
public Producer(Exchanger<Integer> exchanger) {
super();
this.exchanger = exchanger;
}
@Override
public void run() {
Random rand = new Random();
int x=100;
for(int i=1; i<=10; i++) {
try {
int otherThread=exchanger.exchange(x+i);//x+i将被交换到Consumer,otherThread的数据将为y+i
System.out.println(getId()+" get exchange data="+otherThread);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer extends Thread {
Exchanger<Integer> exchanger = null;
public Consumer(Exchanger<Integer> exchanger) {
super();
this.exchanger = exchanger;
}
@Override
public void run() {
int y=50;
for(int i=1; i<=10; i++) {
try {
int fromother=exchanger.exchange(y+i);//y+i将被交换到Producer,fromother的数据将为x+i
System.out.println(getId()+" get exchange data="+fromother);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}