java多线程学习

时间:2022-05-24 07:11:44

基于前面对java中的锁做的小结,本文会对多线程的使用做一个小结:

一、多线程的基本使用

二、多线程的进阶应用


一、多线程的基本使用

1、一些基本概念

进程和线程:主要差别在于它们是不同的操作系统资源管理方式。进程是cpu资源分配的最小单位,线程是cpu调度的最小单位。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。

 同步和异步:是指函数或方法在调用时的结果返回情况。同步是需要等待所有任务完成后返回结果,而异步的瞬间返回并不代表任务完成。

并行和并发:并行是时间上各不影响的同时执行,而并发在时间上是相互应用时间片段,宏观上看是并行的。

临界区:表示一种公共资源,可以被多个线程使用,但是每次只允许一个线程独占。

阻塞和非阻塞:形容多线程之间的相互影响,当一个线程独占临街资源时,其他线程需要等待,导致线程挂起,这就是阻塞。

死锁:指两个或多个线程在执行的过程中,由于资源相互竞争造成阻塞的现象。若无相关操作,他们将被无限期阻塞下去。死锁是一个静态问题,进程会被卡死,但是不会占用cpu。

活锁:是一个动态过程,比如:线程A、B都需要临界资源a和临界资源b,线程A占用a,需要b,线程B占用b,需要a,放弃了资源以后,A又获得了b资源,B又获得了a资源,如此反复,则发生了活锁。

饥饿:指某一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行。

2、线程的基本状态(来源http://www.cnblogs.com/GarfieldEr007/p/5746362.html)

1)新建状态:被new()出来

2)runnable状态:线程start

3)running状态:线程获得时间片,正执行

4)等待状态:线程被wait,释放掉锁的状态

5)等锁状态:线程被notify后,进入等锁池

6)阻塞状态:线程sleep,join或者Io阻塞

7)dead:线程执行完

线程之间的扭转关系可有:

java多线程学习


3、线程的创建和终止

1)常见线程的创建方式:继承Thread,复写run()方法;实现Runnable接口;线程池使用

因为继承只有一个类,所以在某种程度上,实现方式用法更广。当然,考虑到线程创建和销毁的代价,线程池是一种常见的做法,如数据库的连接池等。

2)终止线程:

Thread.stop():会释放掉所有的监管monitor,无论线程执行到哪里,都会立即停止线程,不推荐使用。

线程中断:中断是一种协作机制,调用线程的interrupt方法不一定会中断正在运行的线程,它会要求线程在合适的时机结束自己。每个线程都会有一个boolean的中断状态,interrupt方法只是将状态设置为true,对于非阻塞的线程,只是状态进行了改变,并不一定会立即停止。Thread.sleep,Object.wait,Thread.join这行方法会清除中断状态,Boolean设置为false。

线程挂起suspend和继续执行resume。:suspend不会释放锁,一直占用临界资源,直到被其他线程resume。但是,多线程的执行无法控制先后顺序,如果后来的线程suspend,会导致线程卡死。

yield:yield方法是把自己占有的cpu时间释放掉,然后再和其他线程一起竞争CPU资源,这与sleep不同,sleep会释放掉CPU资源,当下并不会竞争。

join:是保证线程能够执行完成,而不会因为其他线程先运行完结束。

3)守护线程:JVM中优先级最低的线程,如果当前有其他线程,守护线程会在所有线程结束后结束。

4、线程的优先级

Thread定义了线程的优先级是1-10,但类中提供了三个线程的优先级,MIN_PRIORITY、NORMAL和MAX_PRIORITY。并不是优先级高的线程就一定先执行。

5、wait和notify区别
共同点: 
1)都是在多线程的环境下,都可以在程序的调用处阻塞指定的毫秒数,并返回。 
2)wait()和sleep()都可以通过interrupt()方法 打断线程的暂停状态 ,从而使线程立刻抛出InterruptedException。 

不同点:

1)sleep和yield方法都是Thread类方法,wait和notify,notifyAll都是object的方法;

2)sleep睡眠时,保持对象锁,仍然占有该锁;而wait睡眠时,释放对象锁。

3)sleep可以在任何地方使用,并且需要捕获编译异常;wait和notify,notifyAll需要在同步块中使用。

二、多线程中的happen-before

Java语言中有一个“先行发生”(happen—before)的规则,它是Java内存模型中定义的两项操作之间的偏序关系。Java内存模型中的八条可保证happen—before的规则,它们无需任何同步器协助就已经存在。

  • 程序顺序原则:一个线程内保证语义的串行性
  • volatile规则:volatile变量的写,先发生于读,这保证了volatile变量的可见性
  • 锁规则:解锁(unlock)必然发生在随后的加锁(lock)前
  • 传递性:A先于B,B先于C,那么A必然先于C
  • 线程的start()方法先于它的每一个动作
  • 线程的所有操作先于线程的终结(Thread.join())
  • 线程的中断(interrupt())先于被中断线程的代码
  • 对象的构造函数执行结束先于finalize()方法

public class HappenBeforeTest extends Thread {
private boolean flag;

@Override
public void run(){
while(!flag){
System.out.println("thread run");
}
System.out.println("thread stop");

}

public boolean getStop(){
flag = true;
return flag;
}

public static void main(String[] args) throws InterruptedException {
HappenBeforeTest test = new HappenBeforeTest();
test.start();
Thread.sleep(1000);
test.getStop();
Thread.sleep(1000);
}
}
这个程序在client模式下能够正常的打印thread run和thread stop。但是在Server模式下可能将是无限循环。因为虽然getStop函数设定了结束标识,但是线程不一定能取到值,甚至会运行抛出异常。

三、线程中断

中断是通过线程的Thread.interrupt()方法做的,该方法修改了线程中断状态。对于非阻塞线程,中断只是修改了中断状态,并不会停止线程的执行,Thread.interrupted()返回true。如果线程上使用了Thread.sleep(), Object.wait(), Thread.join(),这个线程收到中断信号后, 会抛出InterruptedException, 同时会把中断状态置回为false。