《Java多线程编程核心技术》读后总结

线程

线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元,更多信息参见百度百度-线程。

与创建线程相关的两个类:ThreadRunnable

public class Thread implements Runnable{
    private Runnable target;
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}
  • Runnable接口定义执行行为。
  • Thread实现了Runnable接口,当Thread中指明了受委托的Runnable对象,调用也会调用该Runnable对象的run()方法。
  • Thread封装了对线程管理的常用操作,诸如:线程(状态)调度、线程池、线程优先级等。

两者在功能上是父子集合的关系,通常离开ThreadRunnable对象是无意义的。

线程如何执行

线程是通过调用start()方法正确执行的,很多书籍上指明了直接调用run()是错误做法,那么为什么调用start()是正确的呢?

// 摘选自:Thread.class
    public synchronized void start() {
        if (threadStatus != 0/*NEW*/)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

观察到线程被添加到ThreadGroup中,并调用本地方法start0(),有关JVM的部分本文暂不涉及。
而直接调用run()方法则是在当前线程中直接方法调用,与普通方法调用并无区别。

数据可见性

  • 对象的私有实例数据仅在线程中可见
  • 对象的公开实例数据可在不同的线程中可见
  • 对象的公开静态示例数据可在不同的线程中可见

主要有以下手段,解决后两者引发的多线程问题:
* 使用synchronized
* 使用volatile

volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存与主存间数据做同步。

线程安全包括原子性可见性两个方面,Java的同步机制就是围绕这两个方面来确保线程安全的。

volatile的使用场景是在多个线程中可以感知实例变量被更改了,并且可以获得最新的值使用,也就是用多线程读取共享变量时可以获得最新值使用。

线程间通信

  • 使用Thread.wait()、Thread.notify()
  • 使用ReentrantLock.lock()、ReentrantLock.unlock()
  • 使用Thread.join()
  • 使用ThreadLocal类;

了解这章的知识,需要先了解观察者模式的原理。

调用wait()立即释放锁,调用notify()不释放锁。

假设当线程调用wait()后,不释放锁则意味着独占该锁。那么原本调用notify()的线程将没有执行机会,那就意味着死锁。

public abstract class AbsRunnable implements Runnable {
    protected Object monitor;

    public AbsRunnable(Object monitor) {
        this.monitor = monitor;
    }

    protected void print(Object str) {
        System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()) + "  :  " + getClass().getSimpleName() + "  :  " + str);
    }
}

public class ARunnable extends AbsRunnable {

    public ARunnable(Object monitor) {
        super(monitor);
    }

    @Override
    public void run() {
        synchronized (monitor) {
            try {
                print("wait()");
                monitor.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            print(monitor);
        }
    }
}

public class BRunnable extends AbsRunnable {

    public BRunnable(Object monitor) {
        super(monitor);
    }

    @Override
    public void run() {
        synchronized (monitor) {
            print(monitor);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            monitor.notify();
            print("notify()");
        }
    }
}

public class ClientMain {

    public static void main(String[] args) {
        Object lock = new Object();
        AbsRunnable a = new ARunnable(lock);
        AbsRunnable b = new BRunnable(lock);

        new Thread(a).start();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(b).start();

    }
}

注意调用wait()notify()notifyAll()的前提条件是当前线程已经获得锁,否则将会抛出监视器对象异常。实际中使用时候,需要避免先通知后等待通知的情况。

在某些场合下sleep()wait()给人的感觉很相似,但它们之间是有区别的。最明显的区别是,wait()执行完会立即释放锁,而sleep()不会。


总结

标签:入门级读物工具手册

博主读此书的时间:2018.04.09-2018.04.14

该书的阅读时间约两周,适合对多线程技术欠缺经验的小白用户。多以实例验证为主,告知读者“如何这样写,会引发什么问题”。但是欠缺原理上的解释,可以视为削减版(常用类)的工具手册。

购买地址

《Java多线程编程核心技术》拿起淘宝扫描二维码,即可获取该书的购买地址。
拿起手机扫一扫

你可能感兴趣的:(《Java多线程编程核心技术》读后总结)