Java线程、并发概念整理

来源:互联网 发布:mysql 1267 编辑:程序博客网 时间:2024/06/05 11:56

一、进程和线程
进程: 进程一般由程序、数据集合和进程控制块三部分组成。程序用于描述进程要完成的功能,是控制进程执行的指令集;数据集合是程序在执行时所需要的数据和工作区;程序控制块(Program Control Block,简称PCB),包含进程的描述信息和控制信息,是进程存在的唯一标志。进程由内存空间(代码、数据、进程空间、打开的文件)和一个或多个线程组成。

线程:线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。一个标准的线程由线程ID、当前指令指针(PC)、寄存器和堆栈组成

进程和线程的区别:
1.线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
2.一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
3.进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号),某进程内的线程在其它进程不可见;
4.调度和切换:线程上下文切换比进程上下文切换要快得多。

二、线程状态

新创建(New)、可运行(Runnable)、被阻塞(Blocked)、等待(Waiting)、计时等待(Timed waiting)、被终止(Terminated)
线程被终止的原因:

1. 因为run方法正常退出而自然死亡2. 因为一个没有捕获的异常终止了run方法而意外死亡

三、线程属性

线程优先级
默认情况下,一个线程优先级是继承与它的父线程的。可以调用setPriority方法设置一个线程的优先级
在线程调度器有机会选择新线程的时候,首先选择优先级最高的线程。

守护线程
可以调用setDaemon(true);方法把线程转化为守护线程。守护线程的用途就是为其他线程提供服务。
并且,必须在线程启动之前调用。

未捕获异常处理器
线程的run方法不能抛出任何受查异常
在线程没有捕获到非受查异常而导致线程终止,那么这个异常就会被传播到一个用于未捕获异常的处理器。
这个处理器必须实现Thread.UncaughtExceptionHandler接口,然后实现唯一的方法void uncaughtException(Thread t, Throwable e);
线程可以调用setUncaughtExceptionHandler方法为线程安装一个处理器,也可以调用Thread类的静态方法setUncaughtExceptionHandler方法为所有线程安装一个默认处理器。否则默认处理器为空,如果没有给线程安装处理器的话,那么线程的处理器就是线程组(ThreadGroup)对象

四、并发

当多个线程对用一个数据域进行操作的时候就有可能在操作的过程中的某一个时刻,数据出现紊乱和错误。
锁对象:两种方法
1.使用synchronized关键字
2.使用ReentrantLock类

ReentrantLock类
在把可能存在冲突数据的代码块放在try中,在try之前使用ReentrantLock对象的lock方法进行加锁。在finally代码块中调用对象的unlock方法释放锁。

myLock();try{  ...}finally{  myLock.unlock();}

如果在操作冲突数据的时候有可能存在一直拿着锁不释放的情况,就应该使用条件对象Condition

Class ClassA{    private Condition sufficientFunds;    public ClassA(){         sufficientFunds = myClock.newCondition();         ....    }    ...}

然后在出现这种情况的时候调用sufficientFunds.await();
如果在另一个线程中出现一个可以解决这种情况,则在解决这种情况之后调用sufficientFunds.signalAll();,但是这种方法是唤醒所有等待中的,并且满足条件的线程,让这些线程去争抢已经释放出来的锁

Synchronized关键字
这是java语言内部机制实现的一种同步方式,从Java 1.0开始,每一个对象都有一个内部锁。
可以在需要同步的方法名前面加上Synchronized关键字,在需要阻塞的地方,调用wait()方法。在需要唤醒其他线程的地方调用notify()或者notifyAll()


Volatile
为实例域提供一种免锁机制,如果把一个变量声明为Volatile的话,那么编译器和虚拟机就会知道这个变量会被多个线程同时访问,从而有可能造成脏数据,则它们会为了避免这种情况。但是Volatile不是原子性的


原子性
原子是最小的东西,具有不可分割的特性。在Java并发中的原子性,是Java的某一个操作具有不可分割性(我本人的理解,如有问题,请指出。),如果一个操作不是原子性的,那么久会存在线程安全的问题。这个时候我们就要采用给操作加锁等方式,让这个操作编程原子性的。


死锁
概念:一个或多个线程在等待其他的线程执行的结果才能够执行,而这个线程也因为需要其他线程的结果,从而导致互相等待(阻塞)的结果,这种情况就是死锁
死锁的必要条件:
1. 互斥访问
2. 非抢占
3. 持有并等待
4. 循环等待

五、线程局部变量和锁测试

如果使用线程共享变量来执行一些公共操作,如格式化操作(SimpleDateFormat类),而这些操作又不是线程安全的,那么就可以使用ThreadLocal< T >这个类来构造一个本线程的局部变量

//构造一个线程的局部变量public static final ThrearLocal<SimpleDateFormat> dateFormat = ThreadLocal.withInitial(()->new SimpleDateFormat("yyyy-MM-dd"));//使用get方法获取局部变量的实例SimpleDateFormat format = dateFormat.get();

当一个线程在调用lock方法来获取另一个线程持有的锁时,可能出现另一个线程还没有释放锁,而出现阻塞。则应该调用tryLock()方法,去试图拿到锁,这个方法成功拿到锁之后返回true,反之返回false。如果没有拿到锁,那这个线程不会出现阻塞,还可以干其他的事情。并且可以为这个方法设置一个超时,tryLock(100,TimeUnit.MILLISECODS);


读/写锁
在Java的java.util.concurrent.locks包中,有一个ReentrantReadWriteLock类。这个锁,允许多个线程同时读取数据,而只允许一个线程进行写数据。
1.Lock readLock()
得到一个可以被多个读操作共用的读取锁,会排斥所有写操作。
2.Lock writeLock()
得到一个只能够被一个写操作使用的写入锁,会排斥所有其他的读操作和写操作。

六、阻塞队列

概念:把所有进行对实体对象进行的操作放到一个队列里面,只由一个线程去取出这些操作并执行。整个过程中,有许多线程可以去放入操作,而只有一个线程去执行这些操作。
当试图向一个已满线程去添加元素,或者试图从一个已空的线程中取出元素时,阻塞队列就会导致线程阻塞。

七、线程池

概念:一个线程池中存在大量空闲的线程,为程序的下一个请求提供服务,当一个请求执行之后,线程不会死掉,而是又回到池子中等待下一次请求。
如果需要在程序中创建大量生命期很短的线程,则应该使用线程池。

原创粉丝点击