java 并发

212 阅读5分钟

简述

并发就是多个任务同时执行,对于多处理器设备是多个core同时运行;对于单处理器设备通过时间片的切换,多任务运行。多个处理器也是可以进行任务切换的

进程

是系统的级别的概念等同于我们常说的应用程序,进程具有私有空间与地址空间。进程时间的交互主要通过socketIPC(internal process commucation)、管道等。通过ProcessBuilder可以创建进程

线程 thread

线程进程的一部分,进程内至少存在一个线程。创建线程需要较少的资源。线程共享进程的数据,线程比较高效、但是会存在线程之间干涉的问题

线程的分析

线程的用法有两种:

  1. 创建Thread对象,并初始化任务。简单,没有对象线程管理
  2. 将任务提交给Execute对象。这个是线程管理部分 ##线程相关问题

线程冲突与线程一致性错误

  • 线程冲突:当两个线程对同一个数据进行操作的时候,读写交错在一起。
  • 线程一致性错误:对同一块空间,两个线程看到的结果是不一样的。差生原因是比较多,根本原因是每一个线程都有自己的私有空间,线程不会直接调用公共空间的数据,导致私有空间的数据不一致。要有happen-before关系才可以。

Synchronized方法

同一个对象的Synchronized方法,只允许一个进行调用,其他进程进入block。 ps:构造器不允许synchronized,对象的创建只有一个进程进行,但是构造对象不要过早的暴露出来,给其他进程在没有完成的时候,使用。
synchronized其实就是对象的内部锁与监控锁,锁的对象。当一个进程获取了对象锁后,在释放之前其他进程进入block。所有数据发生了happen-before的关系。
synchronize的另一种用法是对对象进行声明的用法

synchronized(this)//表示调用这个对象的锁

原子操作

表示操作不可以被中断,所以在原子操作过程中,就不存在冲突与内存不一致,(之后不能保证)。原子操作是有java本身自己完成的,应用相对于synchronize简单。如下有两种情况:

  1. 读写对象的引用;大多数的原始类型(除了double与long)
  2. 对于viatile修饰的对象。包含long、double、其他变量。
  3. viatile关键字的作用,1、原子操作。2、防止程序顺序优化。3、写入主内存后,会告之其他线程读的数据无效。
Reads and writes are atomic for reference variables and for most primitive variables (all types except long and double).
Reads and writes are atomic for all variables declared volatile (including long and double variables).


并发

进程的活力

进程能在一定的时间内执行完毕,就是进程具有活力,有两个相关问题:进程死锁与进程饥饿、活锁。

  • 死锁:与其他进程,各自占用相关的资源
  • 进程饥饿、活锁:饥饿是长时间得不到资源,原因是其他进程长时间占用资源。活锁是出现进程之间相互嵌套、相互等待。总的来说是不是死锁是长时间不能运行。

guarded block

保护块的实施意义:对象对应着一个等待集合中,当调用notifyall()会通知所有等待集合中的线程进入blocked状态,请求线程的监控锁,得到后就继续执行(notify()函数的特点是从等待集合中选择任意一个进出入block状态)。这个功能的主要作用是进程之间的调度。

如何进入等待。

  1. 首先获得对象的进程锁。一般是通过synchronize函数
  2. 进入while循环,条件是等待调度的条件。这样做的原因是防止waite函数因为interruput()而退出等待状态。或者notifyall释放的条件不是线程需要的条件
  3. wait()函数,并对异常进行捕获,防止异常跳出while循环
while(!joy) {
        try {
            wait();
        } catch (InterruptedException e) {}
    }

如何释放notifyAll

其他线程拥有这个线程的内部锁后,释放notifyAll函数,将wait集合中的函数进入block等地状态。并设置相应的条件,对wait集合中的线程进程判断。相应的转化关系可以看到java线程状态图。

public synchronized notifyJoy() {
    joy = true;
    notifyAll();
}

线程的状态:

根据Thread的内部类State,java线程有5中状态,注意跟操作系统没有关系

  1. new 创建后没有开始,进入new状态
  2. RUNNIN状态,调用start之后的状态,注意对于操作系统来说,有可能是没有得到cpu的
  3. BLOCKED 等待对象的监控锁
  4. WAIT,调用如下三个函数,等待其他进程给一个指示
Object.wait with no timeout
Thread.join with no timeout
LockSupport.park
  1. 等待一定时间
Thread.sleep
Object.wait with timeout
Thread.join with timeout
LockSupport.parkNanos
LockSupport.parkUntil
  1. 终止,当任务执行完毕,或者从异常退出。
    转换关系如下

其他

后台进程

后台进程的作用:一般用于管理、监控部分。主要特点是后台进程不能独立存活。 如下例子预计打印结果如下。thread进程在main进程结束之后就停止

package concurrent;

public class test1 {


    public static void main(String[] args) {
        System.out.println("**********hello test1");
        Thread thread1=new Thread(){
            @Override
            public void run() {
                while(true){
                  System.out.println("thread1");
                  try {
                       Thread.sleep(1000);
                  } catch (InterruptedException e) {
                       e.printStackTrace();
                  }
                }
            }
        };

        thread1.setDaemon(true);
        thread1.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        System.out.println("**********over test1");
    }
}

打印结果:

**********hello test1
thread1
thread1
thread1
thread1
thread1
**********over test1

Process finished with exit code 0

线程的classloader

这个后面单独出一节关于classloader的内容。

interrupt()方法比较重要

当interrupt会调用权限检查,只有在一个进程组内采用权限。对不同的函数有不同的梵音

  1. 当线程 sleep、join、wait方法中的时候,会收到一个中断,并且interrupt关键字会被清除
  2. 当线程在一个可中断的通道中的时候时,通道会关闭。并且中断状态置位,线程收到一个ClosedByInterruptException异常
  3. 当在选择器中,block的时候,会从选择器中返回
  4. 线程正常运行的时候,中断置位,但是正常运行。

transient vitaile关键字。内存存取数据形式。

viatile已经在原子操作中描述,transient关键字是对于串行化操作的,静态域transient关键字表述的对象不能被保存。