实战Java高并发程序设计记录(一)

java concurrent programming

Posted by Kinsomy on January 16, 2019

第一章

Happen-Before规则

指令重排的原则:

  • 程序顺序原则:一个线程内保证语义的串行性

  • volatile规则:volatile变量的写先发生于读,保证volatile变量的可见性

  • 锁规则:解锁unlock必然发生在随后的加锁lock之前

  • 传递性:A先于B,B先于C,那么A必然先于C

  • 线程的start()先于线程的每一个动作

  • 线程的所有操作先于线程的join()发生

  • 线程的中断(interrupt())先于被中断线程的代码

  • 对象的构造函数执行、结束先于finalize()方法

第二章

1.新建线程

//正确
Thread t1 = new Thread();
t1.start();

//错误
Thread t2 = new Thread();
t2.run();

新建线程需要调用线程的start()方法,方法会新建一个线程并让新建线程执行run()方法,如果直接使用t2.run()方法,会编译通过,也能正常执行,但是它不会新建一个线程,而是在当前线程调用run(),串行执行。

2.终止线程

stop()被标记为废弃,因为该方法太过暴力,会强行终止正在执行的线程,并且释放线程所持有的锁,会引发数据的不一致。

3.中断线程

// 中断线程
public void Thread.interrupt()               // 判断是否被中断
public boolean Thread.isInterrupted()        // 判断是否被中断,并清除当前中断状态
public static boolean Thread.interrupted()   

sleep()方法会抛出InterruptedException中断异常,当在sleep过程中被中断就会抛出,此时会清除中断标记,因此在捕获异常处理时,要手动再次设置中断标记。

Thread t1 = new Thread(){
	@Override
	public void run() {
		while (true){
			if(Thread.currentThread().isInterrupted()){
				break;
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				//中断标记被清除,需要重新设置
				Thread.currentThread().interrupt();
			}
			Thread.yield();
		}
	}
};
t1.start();
t1.interrupt();

4.wait和notify

这两个方法不存在Thread类中,还是在Object里,所有对象都可以调用这两个方法。

public final void wait() throws InterruptedException
public final native void notify()

在一个线程A中调用了obj.wait()方法后,线程A会被阻塞变成等待状态,直到有其他线程调用obj.notify()之后才会继续执行。

wait()方法的调用是会将调用所在线程放到obj对象的等待队列里面,这个队列会保存同一对象调用wait方法的多个线程,当调用obj.notify()之后,会在队列里随机选择一个线程唤醒继续执行。还有一个notifyAll()的方法,它就会唤醒所有在该队列内的线程。

查看源码可以在wait()和notify()方法的的注释里看到,调用wait的当前线程必须持有对象的监视器,也就是需要被synchronzied包裹,调用wait和notify之前获取监视器,调用完之后随即释放。这样做是为了不阻碍其他在object等待的线程执行。

wait和sleep的区别是:wait被调用后会释放目标对象的锁,sleep不会释放任何资源。

5.suspend和resume

不推荐使用suspend挂起线程,这是一个已经被标注为废弃的方法,原因是因为suspend在挂起线程时不会释放任何的锁资源,导致其他线程想要访问被占用的锁时都会阻塞住,直到执行了resume操作,才会继续执行。  同时,由于suspend和resume存在时序问题,必须要限制性suspend再执行resume,一旦resume被提前执行了,就会造成死锁。

6.join和yield

join是等待线程结束,一个线程的输入需要依赖其他线程的输出,所以要等他其他线程结果出来再继续执行,这个时候使用join。

//无限等待,一直阻塞线程
public final void join() throws InterruptedException
//限时等待,过时不候
public final synchronized void join(long millis) throws InterruptedException

查看join()源码:

    public final synchronized void join(long var1) throws InterruptedException {
        long var3 = System.currentTimeMillis();
        long var5 = 0L;
        if (var1 < 0L) {
            throw new IllegalArgumentException("timeout value is negative");
        } else {
            if (var1 == 0L) {
                while(this.isAlive()) {
                    this.wait(0L);
                }
            } else {
                while(this.isAlive()) {
                    long var7 = var1 - var5;
                    if (var7 <= 0L) {
                        break;
                    }

                    this.wait(var7);
                    var5 = System.currentTimeMillis() - var3;
                }
            }

        }
    }

实际上就是在当前线程实例调用wait()方法,执行完成后会调用notifyAll()方法通知其他线程继续执行。

注意:不要在线程上再使用wait和notify,会导致混乱。

yield会让调用线程让出cpu占用,然后后续参与cpu的资源竞争,适合一些优先级低,当前不太需要cpu占用的情况。