Java进程&线程

程序:程序员写的代码,就是代码,不运行好像不会发生什么;

进程:一个进程可以理解为“运行的”一个程序,当我们启动一个java程序后,对应的jvm就会创建一个进程;

线程:jvm有一个进程,然而程序的实际执行是通过线程来完成的,进程之间是相互独立的,而线程之间是共享进程的资源的,就是说,进程是由n个线程组成的,而main函数就是进程创建后启动的主线程,另外,有一个用于垃圾回收的线程也是会事先启动的,所以说,一个java程序运行后,至少包含了2个线程(可能还会有其它的);

 

实现多线程的几种方式:最常用的,继承Thread或者实现Runnable接口,还有我们可能不怎么熟悉的使用ExecutorService,Callable,Future实现有返回结果的多线程,它们都是属于Executor框架中的功能类,可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须实现Runnable接口。执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了,再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了;

线程池概念的好处:pool,顾名思义,就是一个容器,装了很多线程,一般来说,降低资源消耗:通过重用已经创建的线程来降低线程创建和销毁的消耗,提高响应速度:任务到达时不需要等待线程创建就可以立即执行,提高线程的可管理性:线程池可以统一管理、分配、调优和监控,这3点是我们看重的;

Executor框架:Executor框架是指java 5中引入的一系列并发库中与executor相关的一些功能类,其中包括线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等,并发编程的一种编程方式是把任务拆分为一些列的小任务,即Runnable,然后再提交给一个Executor执行,Executor.execute(Runnalbe)。Executor在执行时使用内部的线程池完成操作。CompletionService:调用CompletionService的take方法时,会返回按完成顺序放回任务的结果,CompletionService内部维护了一个阻塞队列BlockingQueue,如果没有任务完成,take()方法也会阻塞;

一个例子:

  1. public class ConcurrentCalculator {
  2.  
  3. private ExecutorService exec;
  4. private int cpuCoreNumber;
  5. private List<Future<Long>> tasks = new ArrayList<Future<Long>>();
  6.  
  7. // 内部类
  8. class SumCalculator implements Callable<Long> {
  9. private int[] numbers;
  10. private int start;
  11. private int end;
  12.  
  13. public SumCalculator(final int[] numbers, int start, int end) {
  14. this.numbers = numbers;
  15. this.start = start;
  16. this.end = end;
  17. }
  18.  
  19. public Long call() throws Exception {
  20. Long sum = 0l;
  21. for (int i = start; i < end; i++) {
  22. sum += numbers[i];
  23. }
  24. return sum;
  25. }
  26. }
  27.  
  28. public ConcurrentCalculator() {
  29. cpuCoreNumber = Runtime.getRuntime().availableProcessors();
  30. exec = Executors.newFixedThreadPool(cpuCoreNumber);
  31. }
  32.  
  33. public Long sum(final int[] numbers) {
  34. // 根据CPU核心个数拆分任务,创建FutureTask并提交到Executor
  35. for (int i = 0; i < cpuCoreNumber; i++) {
  36. int increment = numbers.length / cpuCoreNumber + 1;
  37. int start = increment * i;
  38. int end = increment * i + increment;
  39. if (end > numbers.length)
  40. end = numbers.length;
  41. SumCalculator subCalc = new SumCalculator(numbers, start, end);
  42. FutureTask<Long> task = new FutureTask<Long>(subCalc);
  43. tasks.add(task);
  44. if (!exec.isShutdown()) {
  45. exec.submit(task);
  46. }
  47. }
  48. return getResult();
  49. }
  50.  
  51. /**
  52. * 迭代每个只任务,获得部分和,相加返回
  53. *
  54. * @return
  55. */
  56. public Long getResult() {
  57. Long result = 0l;
  58. for (Future<Long> task : tasks) {
  59. try {
  60. // 如果计算未完成则阻塞
  61. Long subSum = task.get();
  62. result += subSum;
  63. } catch (InterruptedException e) {
  64. e.printStackTrace();
  65. } catch (ExecutionException e) {
  66. e.printStackTrace();
  67. }
  68. }
  69. return result;
  70. }
  71.  
  72. public void close() {
  73. exec.shutdown();
  74. }
  75. }

使用CompletionService改进:

  1. public class ConcurrentCalculator2 {
  2.  
  3. private ExecutorService exec;
  4. private CompletionService<Long> completionService;
  5.  
  6. private int cpuCoreNumber;
  7.  
  8. // 内部类
  9. class SumCalculator implements Callable<Long> {
  10. private int[] numbers;
  11. private int start;
  12. private int end;
  13.  
  14. public SumCalculator(final int[] numbers, int start, int end) {
  15. this.numbers = numbers;
  16. this.start = start;
  17. this.end = end;
  18. }
  19.  
  20. public Long call() throws Exception {
  21. Long sum = 0l;
  22. for (int i = start; i < end; i++) {
  23. sum += numbers[i];
  24. }
  25. return sum;
  26. }
  27. }
  28.  
  29. public ConcurrentCalculator2() {
  30. cpuCoreNumber = Runtime.getRuntime().availableProcessors();
  31. exec = Executors.newFixedThreadPool(cpuCoreNumber);
  32. completionService = new ExecutorCompletionService<Long>(exec);
  33.  
  34. }
  35.  
  36. public Long sum(final int[] numbers) {
  37. // 根据CPU核心个数拆分任务,创建FutureTask并提交到Executor
  38. for (int i = 0; i < cpuCoreNumber; i++) {
  39. int increment = numbers.length / cpuCoreNumber + 1;
  40. int start = increment * i;
  41. int end = increment * i + increment;
  42. if (end > numbers.length)
  43. end = numbers.length;
  44. SumCalculator subCalc = new SumCalculator(numbers, start, end);
  45. if (!exec.isShutdown()) {
  46. completionService.submit(subCalc);
  47.  
  48. }
  49.  
  50. }
  51. return getResult();
  52. }
  53.  
  54. /**
  55. * 迭代每个只任务,获得部分和,相加返回
  56. *
  57. * @return
  58. */
  59. public Long getResult() {
  60. Long result = 0l;
  61. for (int i = 0; i < cpuCoreNumber; i++) {
  62. try {
  63. Long subSum = completionService.take().get();
  64. result += subSum;
  65. } catch (InterruptedException e) {
  66. e.printStackTrace();
  67. } catch (ExecutionException e) {
  68. e.printStackTrace();
  69. }
  70. }
  71. return result;
  72. }
  73.  
  74. public void close() {
  75. exec.shutdown();
  76. }
  77. }

ThreadPoolExecutor:它是一个ExecutorService,它使用可能的几个池线程之一执行每个提交的任务,通常使用Executors工厂方法配置;当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程,当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行,当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务,当提交任务数超过maximumPoolSize时,新提交任务由RejectedExecutionHandler处理,当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程,当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭;

Executors提供的线程池配置方案,从而生成不同的ExecutorService;

注意非阻塞队列和阻塞队列,无界和有界队列:用ThreadPoolExecutor自定义线程池,看线程是的用途,如果任务量不大,可以用无界队列,如果任务量非常大,要用有界队列,防止OOM,如果任务量很大,还要求每个任务都处理成功,要对提交的任务进行阻塞提交,重写拒绝机制,改为阻塞提交,保证不抛弃一个任务,最大线程数一般设为2N+1最好,N是CPU核数,核心线程数,看应用,如果是任务,一天跑一次,设置为0,合适,因为跑完就停掉了,如果是常用线程池,看任务量,是保留一个核心还是几个核心线程数,如果要获取任务执行结果,用CompletionService,但是注意,获取任务的结果的要重新开一个线程获取,如果在主线程获取,就要等任务都提交后才获取,就会阻塞大量任务结果,队列过大OOM,所以最好异步开个线程获取结果;

线程安全:线程安全无非是要控制多个线程对某个资源的访问或修改,感觉这个说的非常明了,对于java的内存模型来说,要解决可见性和有序性;

那么,何谓可见性?多个线程之间是不能互相传递数据通信的,它们之间的沟通只能通过共享变量来进行。Java内存模型(JMM)规定了jvm有主内存,主内存是多个线程共享的。当new一个对象的时候,也是被分配在主内存中,每个线程都有自己的工作内存,工作内存存储了主存的某些对象的副本,当然线程的工作内存大小是有限制的。当线程操作某个对象时,执行顺序如下:

(1) 从主存复制变量到当前工作内存 (read and load);

(2) 执行代码,改变共享变量值 (use and assign);

(3) 用工作内存数据刷新主存相关内容 (store and write);

JVM规范定义了线程对主存的操作指令:read,load,use,assign,store,write。当一个共享变量在多个线程的工作内存中都有副本时,如果一个线程修改了这个共享变量,那么其他线程应该能够看到这个被修改后的值,这就是多线程的可见性问题。

那么,什么是有序性呢?线程在引用变量时不能直接从主内存中引用,如果线程工作内存中没有该变量,则会从主内存中拷贝一个副本到工作内存中,这个过程为read-load,完成后线程会引用该副本。当同一线程再度引用该字段时,有可能重新从主存中获取变量副本(read-load-use),也有可能直接引用原来的副本(use),也就是说read,load,use顺序可以由JVM实现系统决定。

线程不能直接为主存中中字段赋值,它会将值指定给工作内存中的变量副本(assign),完成后这个变量副本会同步到主存储区(store- write),至于何时同步过去,根据JVM实现系统决定,有的字段,则会从主内存中将该字段赋值到工作内存中,这个过程为read-load,完成后线程会引用该变量副本,当同一线程多次重复对字段赋值时,比如:

for(int i=0;i<10;i++)

a++;

线程有可能只对工作内存中的副本进行赋值,只到最后一次赋值后才同步到主存储区,所以assign,store,weite顺序可以由JVM实现系统决定。假设有一个共享变量x,线程a执行x=x+1。从上面的描述中可以知道x=x+1并不是一个原子操作,它的执行过程如下:

1:从主存中读取变量x副本到工作内存;

2:给x加1;

3:将x加1后的值写回主存;

如果另外一个线程b执行x=x-1,执行过程如下:

1:从主存中读取变量x副本到工作内存;

2:给x减1;

3:将x减1后的值写回主存 ;

那么显然,最终的x的值是不可靠的。假设x现在为10,线程a加1,线程b减1,从表面上看,似乎最终x还是为10,但是多线程情况下会有这种情况发生:

1:线程a从主存读取x副本到工作内存,工作内存中x值为10;

2:线程b从主存读取x副本到工作内存,工作内存中x值为10;

3:线程a将工作内存中x加1,工作内存中x值为11;

4:线程a将x提交主存中,主存中x为11;

5:线程b将工作内存中x值减1,工作内存中x值为9;

6:线程b将x提交到中主存中,主存中x为9;

同样,x有可能为11,如果x是一个银行账户,线程a存款,线程b扣款,显然这样是有严重问题的,要解决这个问题,必须保证线程a和线程b是有序执行的,并且每个线程执行的加1或减1是一个原子操作。看看下面代码:

  1. public class Account {
  2.  
  3. private int balance;
  4.  
  5. public Account(int balance) {
  6. this.balance = balance;
  7. }
  8.  
  9. public int getBalance() {
  10. return balance;
  11. }
  12.  
  13. public void add(int num) {
  14. balance = balance + num;
  15. }
  16.  
  17. public void withdraw(int num) {
  18. balance = balance - num;
  19. }
  20.  
  21. public static void main(String[] args) throws InterruptedException {
  22. Account account = new Account(1000);
  23. Thread a = new Thread(new AddThread(account, 20), "add");
  24. Thread b = new Thread(new WithdrawThread(account, 20), "withdraw");
  25. a.start();
  26. b.start();
  27. a.join();
  28. b.join();
  29. System.out.println(account.getBalance());
  30. }
  31.  
  32. static class AddThread implements Runnable {
  33. Account account;
  34. int amount;
  35.  
  36. public AddThread(Account account, int amount) {
  37. this.account = account;
  38. this.amount = amount;
  39. }
  40.  
  41. public void run() {
  42. for (int i = 0; i < 200000; i++) {
  43. account.add(amount);
  44. }
  45. }
  46. }
  47.  
  48. static class WithdrawThread implements Runnable {
  49. Account account;
  50. int amount;
  51.  
  52. public WithdrawThread(Account account, int amount) {
  53. this.account = account;
  54. this.amount = amount;
  55. }
  56.  
  57. public void run() {
  58. for (int i = 0; i < 100000; i++) {
  59. account.withdraw(amount);
  60. }
  61. }
  62. }
  63. }

第一次执行结果为10200,第二次执行结果为1060,每次执行的结果都是不确定的,因为线程的执行顺序是不可预见的。这是java同步产生的根源,synchronized关键字保证了多个线程对于同步块是互斥的,synchronized作为一种同步手段,解决java多线程的执行有序性和内存可见性,而volatile关键字之解决多线程的内存可见性问题。后面将会详细介绍。

synchronized关键字:

上面说了,java用synchronized关键字做为多线程并发环境的执行有序性的保证手段之一。当一段代码会修改共享变量,这一段代码成为互斥区或 临界区,为了保证共享变量的正确性,synchronized标示了临界区。典型的用法如下:

synchronized(锁){

临界区代码

}

为了保证银行账户的安全,可以操作账户的方法如下:

  1. public synchronized void add(int num) {
  2. balance = balance + num;
  3. }
  4. public synchronized void withdraw(int num) {
  5. balance = balance - num;
  6. }

刚才不是说了synchronized的用法是这样的吗:

synchronized(锁){

临界区代码

}

那么对于public synchronized void add(int num)这种情况,意味着什么呢?其实这种情况,锁就是这个方法所在的对象。同理,如果方法是public static synchronized void add(int num),那么锁就是这个方法所在的class。

理论上,每个对象都可以做为锁,但一个对象做为锁时,应该被多个线程共享,这样才显得有意义,在并发环境下,一个没有共享的对象作为锁是没有意义的。假如有这样的代码:

  1. public class ThreadTest{
  2. public void test(){
  3. Object lock=new Object();
  4. synchronized (lock){
  5. //do something
  6. }
  7. }
  8. }

lock变量作为一个锁存在根本没有意义,因为它根本不是共享对象,每个线程进来都会执行Object lock=new Object();每个线程都有自己的lock,根本不存在锁竞争。

每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列,就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程,当一个被线程被唤醒 (notify)后,才会进入到就绪队列,等待cpu的调度。当一开始线程a第一次执行account.add方法时,jvm会检查锁对象account的就绪队列是否已经有线程在等待,如果有则表明account的锁已经被占用了,由于是第一次运行,account的就绪队列为空,所以线程a获得了锁,
执行account.add方法。如果恰好在这个时候,线程b要执行account.withdraw方法,因为线程a已经获得了锁还没有释放,所以线程b要进入account的就绪队列,等到得到锁后才可以执行。

一个线程执行临界区代码过程如下:

1:获得同步锁;

2:清空工作内存;

3:从主存拷贝变量副本到工作内存;

4:对这些变量计算;

5:将变量从工作内存写回到主存;

6:释放锁;

可见,synchronized既保证了多线程的并发有序性,又保证了多线程的内存可见性(也有缺陷哦,性能,死锁都是问题)。

生产者/消费者模式(用锁):

生产者/消费者模式其实是一种很经典的线程同步模型,很多时候,并不是光保证多个线程对某共享资源操作的互斥性就够了,往往多个线程之间都是有协作的。

假设有这样一种情况,有一个桌子,桌子上面有一个盘子,盘子里只能放一颗鸡蛋,A专门往盘子里放鸡蛋,如果盘子里有鸡蛋,则一直等到盘子里没鸡蛋,B专门从盘子里拿鸡蛋,如果盘子里没鸡蛋,则等待直到盘子里有鸡蛋。其实盘子就是一个互斥区,每次往盘子放鸡蛋应该都是互斥的,A的等待其实就是主动放弃锁,B等待时还要提醒A放鸡蛋。

如何让线程主动释放锁?

很简单,调用锁的wait()方法就好。wait方法是从Object来的,所以任意对象都有这个方法。看这个代码片段:

  1. Object lock=new Object();//声明了一个对象作为锁
  2. synchronized (lock) {
  3. balance = balance - num;
  4. //这里放弃了同步锁,好不容易得到,又放弃了
  5. lock.wait();
  6. }

如果一个线程获得了锁lock,进入了同步块,执行lock.wait(),那么这个线程会进入到lock的阻塞队列。如果调用lock.notify()则会通知阻塞队列的某个线程进入就绪队列。

声明一个盘子,只能放一个鸡蛋:

  1. import java.util.ArrayList;
  2. import java.util.List;
  3.  
  4. public class Plate {
  5.  
  6. List<Object> eggs = new ArrayList<Object>();
  7.  
  8. public synchronized Object getEgg() {
  9. while(eggs.size() == 0) {
  10. try {
  11. wait();
  12. } catch (InterruptedException e) {
  13. }
  14. }
  15.  
  16. Object egg = eggs.get(0);
  17. eggs.clear();// 清空盘子
  18. notify();// 唤醒阻塞队列的某线程到就绪队列
  19. System.out.println("拿到鸡蛋");
  20. return egg;
  21. }
  22.  
  23. public synchronized void putEgg(Object egg) {
  24. while(eggs.size() > 0) {
  25. try {
  26. wait();
  27. } catch (InterruptedException e) {
  28. }
  29. }
  30. eggs.add(egg);// 往盘子里放鸡蛋
  31. notify();// 唤醒阻塞队列的某线程到就绪队列
  32. System.out.println("放入鸡蛋");
  33. }
  34.  
  35. static class AddThread extends Thread{
  36. private Plate plate;
  37. private Object egg=new Object();
  38. public AddThread(Plate plate){
  39. this.plate=plate;
  40. }
  41.  
  42. public void run(){
  43. for(int i=0;i<5;i++){
  44. plate.putEgg(egg);
  45. }
  46. }
  47. }
  48.  
  49. static class GetThread extends Thread{
  50. private Plate plate;
  51. public GetThread(Plate plate){
  52. this.plate=plate;
  53. }
  54.  
  55. public void run(){
  56. for(int i=0;i<5;i++){
  57. plate.getEgg();
  58. }
  59. }
  60. }
  61.  
  62. public static void main(String args[]){
  63. try {
  64. Plate plate=new Plate();
  65. Thread add=new Thread(new AddThread(plate));
  66. Thread get=new Thread(new GetThread(plate));
  67. add.start();
  68. get.start();
  69. add.join();
  70. get.join();
  71. } catch (InterruptedException e) {
  72. e.printStackTrace();
  73. }
  74. System.out.println("测试结束");
  75. }
  76. }

执行结果:

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

测试结束

声明一个Plate对象为plate,被线程A和线程B共享,A专门放鸡蛋,B专门拿鸡蛋。假设

1:开始,A调用plate.putEgg方法,此时eggs.size()为0,因此顺利将鸡蛋放到盘子,还执行了notify()方法,唤醒锁的阻塞队列的线程,此时阻塞队列还没有线程;

2:又有一个A线程对象调用plate.putEgg方法,此时eggs.size()不为0,调用wait()方法,自己进入了锁对象的阻塞队列;

3:此时,来了一个B线程对象,调用plate.getEgg方法,eggs.size()不为0,顺利的拿到了一个鸡蛋,还执行了notify()方法,唤
醒锁的阻塞队列的线程,此时阻塞队列有一个A线程对象,唤醒后,它进入到就绪队列,就绪队列也就它一个,因此马上得到锁,开始往盘子里放鸡蛋,此时盘子是 空的,因此放鸡蛋成功;

4:假设接着来了线程A,就重复2;假设来料线程B,就重复3;

整个过程都保证了放鸡蛋,拿鸡蛋,放鸡蛋,拿鸡蛋。

volatile关键字:

volatile是java提供的一种同步手段,只不过它是轻量级的同步,为什么这么说,因为volatile只能保证多线程的内存可见性,不能保证多线程的执行有序性。而最彻底的同步要保证有序性和可见性,例如synchronized。任何被volatile修饰的变量,都不拷贝副本到工作内存,任何修改都及时写在主存。因此对于Valatile修饰的变量的修改,所有线程马上就能看到,但是volatile不能保证对变量的修改是有序的。什么意思呢?假如有这样的代码:

  1. public class VolatileTest{
  2. public volatile int a;
  3. public void add(int count){
  4. a=a+count;
  5. }
  6. }

当一个VolatileTest对象被多个线程共享,a的值不一定是正确的,因为a=a+count包含了好几步操作,而此时多个线程的执行是无序的,因为没有任何机制来保证多个线程的执行有序性和原子性。volatile存在的意义是,任何线程对a的修改,都会马上被其他线程读取到,因为直接操作主存,没有线程对工作内存和主存的同步。所以,volatile的使用场景是有限的,在有限的一些情形下可以使用volatile变量替代锁。要使volatile变量提供理想的线程安全,必须同时满足下面两个条件:

1)对变量的写操作不依赖于当前值;

2)该变量没有包含在具有其他变量的不变式中;

volatile只保证了可见性,所以Volatile适合直接赋值的场景,如:

  1. public class VolatileTest{
  2. public volatile int a;
  3. public void setA(int a){
  4. this.a=a;
  5. }
  6. }

在没有volatile声明时,多线程环境下,a的最终值不一定是正确的,因为this.a=a;涉及到给a赋值和将a同步回主存的步骤,这个顺序可能被打乱。如果用volatile声明了,读取主存副本到工作内存和同步a到主存的步骤,相当于是一个原子操作。所以简单来说,volatile适合这种场景:一个变量被多个线程共享,线程直接给这个变量赋值。这是一种很简单的同步场景,这时候使用volatile的开销将会非常小。

原子类:AtomicInteger:

源码:

  1. /*
  2. * @(#)AtomicInteger.java 1.11 06/06/15
  3. *
  4. * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7.  
  8. package java.util.concurrent.atomic;
  9. import sun.misc.Unsafe;
  10.  
  11. /**
  12. * An {@code int} value that may be updated atomically. See the
  13. * {@link java.util.concurrent.atomic} package specification for
  14. * description of the properties of atomic variables. An
  15. * {@code AtomicInteger} is used in applications such as atomically
  16. * incremented counters, and cannot be used as a replacement for an
  17. * {@link java.lang.Integer}. However, this class does extend
  18. * {@code Number} to allow uniform access by tools and utilities that
  19. * deal with numerically-based classes.
  20. *
  21. * @since 1.5
  22. * @author Doug Lea
  23. */
  24. public class AtomicInteger extends Number implements java.io.Serializable {
  25. private static final long serialVersionUID = 6214790243416807050L;
  26.  
  27. // setup to use Unsafe.compareAndSwapInt for updates
  28. private static final Unsafe unsafe = Unsafe.getUnsafe();
  29. private static final long valueOffset;
  30.  
  31. static {
  32. try {
  33. valueOffset = unsafe.objectFieldOffset
  34. (AtomicInteger.class.getDeclaredField("value"));
  35. } catch (Exception ex) { throw new Error(ex); }
  36. }
  37.  
  38. private volatile int value;
  39.  
  40. /**
  41. * Creates a new AtomicInteger with the given initial value.
  42. *
  43. * @param initialValue the initial value
  44. */
  45. public AtomicInteger(int initialValue) {
  46. value = initialValue;
  47. }
  48.  
  49. /**
  50. * Creates a new AtomicInteger with initial value {@code 0}.
  51. */
  52. public AtomicInteger() {
  53. }
  54.  
  55. /**
  56. * Gets the current value.
  57. *
  58. * @return the current value
  59. */
  60. public final int get() {
  61. return value;
  62. }
  63.  
  64. /**
  65. * Sets to the given value.
  66. *
  67. * @param newValue the new value
  68. */
  69. public final void set(int newValue) {
  70. value = newValue;
  71. }
  72.  
  73. /**
  74. * Eventually sets to the given value.
  75. *
  76. * @param newValue the new value
  77. * @since 1.6
  78. */
  79. public final void lazySet(int newValue) {
  80. unsafe.putOrderedInt(this, valueOffset, newValue);
  81. }
  82.  
  83. /**
  84. * Atomically sets to the given value and returns the old value.
  85. *
  86. * @param newValue the new value
  87. * @return the previous value
  88. */
  89. public final int getAndSet(int newValue) {
  90. for (;;) {
  91. int current = get();
  92. if (compareAndSet(current, newValue))
  93. return current;
  94. }
  95. }
  96.  
  97. /**
  98. * Atomically sets the value to the given updated value
  99. * if the current value {@code ==} the expected value.
  100. *
  101. * @param expect the expected value
  102. * @param update the new value
  103. * @return true if successful. False return indicates that
  104. * the actual value was not equal to the expected value.
  105. */
  106. public final boolean compareAndSet(int expect, int update) {
  107. return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
  108. }
  109.  
  110. /**
  111. * Atomically sets the value to the given updated value
  112. * if the current value {@code ==} the expected value.
  113. *
  114. * <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
  115. * and does not provide ordering guarantees, so is only rarely an
  116. * appropriate alternative to {@code compareAndSet}.
  117. *
  118. * @param expect the expected value
  119. * @param update the new value
  120. * @return true if successful.
  121. */
  122. public final boolean weakCompareAndSet(int expect, int update) {
  123. return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
  124. }
  125.  
  126. /**
  127. * Atomically increments by one the current value.
  128. *
  129. * @return the previous value
  130. */
  131. public final int getAndIncrement() {
  132. for (;;) {
  133. int current = get();
  134. int next = current + 1;
  135. if (compareAndSet(current, next))
  136. return current;
  137. }
  138. }
  139.  
  140. /**
  141. * Atomically decrements by one the current value.
  142. *
  143. * @return the previous value
  144. */
  145. public final int getAndDecrement() {
  146. for (;;) {
  147. int current = get();
  148. int next = current - 1;
  149. if (compareAndSet(current, next))
  150. return current;
  151. }
  152. }
  153.  
  154. /**
  155. * Atomically adds the given value to the current value.
  156. *
  157. * @param delta the value to add
  158. * @return the previous value
  159. */
  160. public final int getAndAdd(int delta) {
  161. for (;;) {
  162. int current = get();
  163. int next = current + delta;
  164. if (compareAndSet(current, next))
  165. return current;
  166. }
  167. }
  168.  
  169. /**
  170. * Atomically increments by one the current value.
  171. *
  172. * @return the updated value
  173. */
  174. public final int incrementAndGet() {
  175. for (;;) {
  176. int current = get();
  177. int next = current + 1;
  178. if (compareAndSet(current, next))
  179. return next;
  180. }
  181. }
  182.  
  183. /**
  184. * Atomically decrements by one the current value.
  185. *
  186. * @return the updated value
  187. */
  188. public final int decrementAndGet() {
  189. for (;;) {
  190. int current = get();
  191. int next = current - 1;
  192. if (compareAndSet(current, next))
  193. return next;
  194. }
  195. }
  196.  
  197. /**
  198. * Atomically adds the given value to the current value.
  199. *
  200. * @param delta the value to add
  201. * @return the updated value
  202. */
  203. public final int addAndGet(int delta) {
  204. for (;;) {
  205. int current = get();
  206. int next = current + delta;
  207. if (compareAndSet(current, next))
  208. return next;
  209. }
  210. }
  211.  
  212. /**
  213. * Returns the String representation of the current value.
  214. * @return the String representation of the current value.
  215. */
  216. public String toString() {
  217. return Integer.toString(get());
  218. }
  219.  
  220. public int intValue() {
  221. return get();
  222. }
  223.  
  224. public long longValue() {
  225. return (long)get();
  226. }
  227.  
  228. public float floatValue() {
  229. return (float)get();
  230. }
  231.  
  232. public double doubleValue() {
  233. return (double)get();
  234. }
  235.  
  236. }

compareAndSet调用Unsafe来实现:

private static final Unsafe unsafe = Unsafe.getUnsafe();

compareAndSet方法首先判断当前值是否等于current;

如果当前值 = current
,说明AtomicInteger的值没有被其他线程修改;

如果当前值 != current,说明AtomicInteger的值被其他线程修改了,这时会再次进入循环重新比较;

java提供的原子操作可以原子更新的基本类型有以下三个:

1,AtomicBoolean;

2,AtomicInteger;

3,AtomicLong;

java提供的原子操作,还可以原子更新以下类型的值:

1,原子更新数组,Atomic包提供了以下几个类:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray;

2,原子更新引用类型,也就是更新实体类的值,比如AtomicReference<User>:

AtomicReference:原子更新引用类型的值;

AtomicReferenceFieldUpdater:原子更新引用类型里的字段;

AtomicMarkableReference:原子更新带有标记位的引用类型;

3,原子更新字段值:

AtomicIntegerFieldUpdater:原子更新整形的字段的更新器;

AtomicLongFieldUpdater:原子更新长整形的字段的更新器;

AtomicStampedReference:原子更新带有版本号的引用类型的更新器;

下一篇,未完待续......

Java进程&线程(一)的更多相关文章

  1. Java进程&线程(整理)

    Java进程&线程 程序:程序员写的代码,就是代码,不运行好像不会发生什么: 进程:一个进程可以理解为"运行的"一个程序,当我们启动一个java程序后,对应的jvm就会创建 ...

  2. 【Java】PS-查看Java进程-线程数

    PS-查看Java进程-线程数 ps 线程 个数_百度搜索 查看进程的线程数命令 - CSDN博客 java命令行运行jar里的main类 - coderland - 博客园

  3. java进程/线程;堆和栈;多线程

    一.进程和线程 进程:在内存中运行的应用程序,一个exe是一个进程. 如:ps -exf  可以查看各个应用的进程,其中ppid为父进程: ps aux | egrep '(cron|syslog)' ...

  4. JAVA 进程线程详解

    线程和进程 一.进程 进程是指运行中的程序,比如我们使用QQ,就启动该进程分配内存空间. 进程是程序的一次执行过程,或是正在运行的一个程序.是一个动态的过程:有它自升的产生,存在和消亡的过程 二.线程 ...

  5. Java进程线程笔记

    什么是并行和并发? 并发和并行是即相似又有区别:(微观) 并行:指两个或多个事件在同一时刻发生: 强调的是时间点. 并发:指两个或多个事件在同一时间段内发生: 强调的是时间段. 进程和线程的区别? 进 ...

  6. Java进程线程理解

    一个进程包括由操作系统分配的内存空间,包含一个或多个线程.一个线程不能独立的存在,它必须是进程的一部分.一个进程一直运行,直到所有的非守护线程都结束运行后才能结束. 多线程能满足程序员编写高效率的程序 ...

  7. 查看JAVA进程中哪个线程CPU消耗最高

    一,在centos linux 上查看进程占用cpu过高 top  shift+h 查看哪个进程程消耗最高     二,查看JAVA进程中哪个线程消耗最高   2.1 导出java运行的线程信息   ...

  8. jps查看java进程中哪个线程在消耗系统资源

    jps或ps -ef|grep java可以看到有哪些java进程,这个不用说了.但值得一提的是jps命令是依赖于/tmp下的某些文件 的. 而某些操作系统,定期会清理掉/tmp下的文件,导致jps无 ...

  9. -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中

     本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁  sleep()和wait()方法的区别 为什么wait( ...

随机推荐

  1. for语句,range() 函式

    for 语句在任意序列 (列表或者字符串) 中迭代时, 总是按照元素在序列中的出现顺序依次迭代. 2 ... a = ['cat', 'window', 'defenestrate']3 >&g ...

  2. socket 简单了解

    网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket.   建立网络通信连接至少要一对端口号(socket).socket本质是编程接口(API),对TCP/IP的 ...

  3. Number.toLocalString() js

    地址链接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocal ...

  4. flask 手机号码正则匹配的简单操作

    1 导包 Import re 2 匹配邮箱 ret=re.match("[a-zA-Z0-9]{4,20}@163\.com",字符串) If ret: print(ret.gro ...

  5. JavaScript 之 预编译 作用域,作用域链

    第一次写博客,本来是学习jQuery遇到闭包问题,发现并没有理解闭包,发现闭包牵扯的知识点太多.复习了一遍(发现自己该记住的全忘了)写在博客里,自己也是小白,希望大神们指点迷津,必将感激不尽. 我们知 ...

  6. .NET数据采集

    public string GetHttpData(string Url) { string sException = null; string sRslt = null; WebResponse o ...

  7. vue2.0 微信分享

    需求:首页,列表页,详情页的分享,活动页分享并进行相关操作,比如分享一次活动次数加1 首先:阅读微信开发文档:https://mp.weixin.qq.com/wiki?t=resource/res_ ...

  8. [USACO06JAN]树林The Grove

    树木(grove)Time Limit: 1Sec Memory Limit: 64 MB[Description]牧场里有一片树林,林子里没有坑.贝茜很想知道,最少需要多少步能围绕树林走一圈,最后回 ...

  9. django中云存储静态文件

    Django自带文件存储系统存储在本地文件夹,如果我们将文件存储在云端,需要自定义文件存储系统. 自定义文件存储系统需要继承django.core.files.storage.Storage from ...

  10. SignalR 行实时通信遇到的

    SignalR可用于向ASP.NET应用程序添加任何类型的“实时”Web功能.虽然聊天经常被用作示例,但您可以做更多的事情.每当用户刷新网页以查看新数据,或者页面实现Ajax长轮询以检索新数据时,都可 ...