Java多线程编程核心技术

目录

停止线程

终止正在运行的线程:

暂停恢复线程

多线程同步技术

synchronized

volatile

wait()/notify()

join()

ThreadLocal类

ReentrantLock类

Condition.await()/signal()

ReentrantReadWriteLock类

定时器Timer

实现线程安全的单例模式

线程的状态

线程异常处理


停止线程

终止正在运行的线程:

  1. 使用退出标记,程序正常退出
  2. 使用stop方法强行退出(暴力停止法),过时方法,强行停止可能导致数据不一致问题
  3. 使用interrupt中断线程(通常是异常法,sleep法)

interrupt()方法仅仅是在当前线程中打了一个停止的标签,不会马上就停下来。如果线程中包含sleep()或wait()会被停止。

interrupted():测试当前线程是否已经中断,执行后具有将状态标志置清除为false的功能。 isInterrupted():测试线程是否已经中断,但不清除状态标志。

无论是thread.interrupted()还是Thread.interrupted(),都指调用这个方法的当前线程。

停止线程异常法

    /**
     * 停止线程,异常法
     * */
    @Test
    public void interruptedTest() throws Exception {
        class MyThread extends Thread {
            @Override
            public void run() {
                try {
                    for (int i = 0; i < 500000; ++i) {
                        if (this.isInterrupted()) {
                            throw new InterruptedException();
                        }

                        System.out.println(i);
                    }
                } catch (InterruptedException ex) {
                    System.out.println("线程被终止!");
                }
            }
        }

        MyThread myThread = new MyThread();
        myThread.start();
        Thread.sleep(2000);
        myThread.interrupt();

        /**
         * output
           ...
           ...
           73186
           73187
           73188
           73189
           73190
           73191
           73192
           线程被终止!
         */
    }

暂停恢复线程

suspend()暂停线程 resume()恢复线程


多线程同步技术

synchronized

synchronized可锁重入,线程在执行带有synchronized的方法时,可以再调用对象的其他synchronized方法。

  1. 如果不可锁重入,就会造成死锁。
  2. 可重入锁支持在继承的环境中。
  3. 出现异常锁自动释放。
  4. synchronized修饰的方法继承的子类不具有该属性。
  5. synchronized方法用在静态方法上,是对这个类对应的Class类进行加锁。而在非静态方法上,是对这个类的对象加锁。

volatile

强制从公共内存中读取变量的值,保证线程对变量的可见性。 对单个volatile变量的读写具有原子性(指实际底层指令原子性),而对于多个volatile变量复核操作不具有原子性。

所谓volatile不支持原子性,指的是不支持对多条源代码事务的原子性,诶,毕竟volatile只修饰变量又不修饰代码块,当然不能保证上层源代码事务的原子性了,但是一条源代码可能被拆分为多条汇编指令,volatile就能保证这多条汇编指令事务是原子性的。哪些直接说volatile不支持原子性是不是有点太笼统了?!毕竟volatile只修饰变量又不修饰代码块。

volatile与synchronized的区别:

  1. volatile只能修饰变量;synchronized可以修饰方法,代码块。
  2. 多线程访问volatile不会阻塞;而synchronized会阻塞。
  3. volatile保证数据的可见性,但不能保证原子性;而synchronized保证可见性和原子性(因为他会将cache和内存的数据做同步)。
  4. volatile解决的是变量在多个线程之间的可见性;而synchronized是解决多个线程之间访问资源的同步性。

i++并不是一个原子操作,分为:

  1. 从内存取i
  2. +1计算
  3. 写回内存 int i,多个线程对i做自增操作(i++),不能保证原子性。

volatile int i,多个线程对i做自增操作(i++),能保证原子性。

volatile的使用场景是在多个线程中可以感知实例变量被更改了。

wait()/notify()

wait()和notify()方法必须用在synchronized的临界区内。 wait()方法会释放锁。wait()状态时,当对线程发起interrupt(),线程出现中断异常InterruptException终止线程。 notify()方法执行后,不会立即释放锁,而是退出synchronized块时释放。

线程出现阻塞的5中情况:

  1. 调用sleep()
  2. 调用了阻塞式的IO
  3. 调用wait()方法,等待notify通知
  4. 等待获取同步监视器(锁等待)
  5. 调用了suspend()挂起线程

wait()和sleep()的区别:

  1. 二者所属的类不同。wait()属于Object类;sleep()属于Thread类。
  2. 二者都会让出cpu;
  3. wait()会释放对象锁;sleep()不会释放对象锁。

join()

join()使当前线程阻塞,等待子线程返回。

join与synchronized的区别: join()在内部使用wait方法等待;而synchronized使用对象监视器做同步。

ThreadLocal类

使线程拥有一个私有变量,通常设计为public static工具类形式。

使用InheritableThreadLocal类可以让子线程从父线程中取得值。类似Log4j中的MDC。

ReentrantLock类

new ReentrantLock(true)指定为公平锁,缺省为非公平锁。 lock()/unlock() unlock()通常放在finally{}代码块中,发生异常时,释放锁。

synchronized与wait()、notify()、notifyAll()实现等待通知,ReentrantLock也可以借助Condition实现等待通知模式,而且可以实现选择性通知。

Condition.await()/signal()

与wait()/notify()的用法相同,condition.await()/singal()的也调用必须获得同步监视器,不同的是使用使用lock.lock()获得。

getHoldCount() 表示当前线程保持此锁的个数(锁重入数)。 getQueueLength() 返回等待此锁的估计数。 getWaitQueueLength(condition) 返回condition条件的await()等待线程的估计数。

hasQueueThread(threadA) 表示threadA是否在等待lock锁。 hasQueueThreads()表示是否有线程在等待此锁。 hasWaiters(condition) 表示是否有线程在等待此锁的condition的signal。

isFair() 是否公平锁。 isHeldByCurrentThread() 当前线程是否持有该锁。 isLocked() 该锁是否被持有

lockInterruptibly() 如果线程未被中断,则获取锁定,否则抛出异常。 tryLock() 尝试取锁 tryLock(long timeout, TimeUnit unit) 尝试取锁,超时返回

ReentrantReadWriteLock类

读读不互斥,读写互斥,写写互斥。即多个读线程不互斥,...

定时器Timer

Timer类用于设置计划任务,创建一个Timer就会创建一个线程。 TimerTask类用于封装新任务

Timer默认不是守护线程,任务执行完了不会退出。

timer.schedule(task, runDate) 在runDate时间点(Date)执行,task任务。 timer.schedule(task, runDate, period) 在runDate时间点执行task任务,周期为period执行一次,单位是ms。

对于周期性任务,如果task任务的执行时长比period长,称为延时,延时情况下,下一次任务的执行时间是上次任务结束时间作为参考;如果不延时,则是下一次任务的执行时间是上一次的开始时间加上period时间。

scheduleAtFixedRate与schedule的区别: scheduleAtFixedRate具有追赶性,如果runDate比现在时间早,那么它会按照比率将runDate到现在错过的任务补充执行。

task.cancel() 从Timer计划任务中移除task任务。 timer.cancel() 将Timer计划任务中的所有任务移除,并且Timer线程被销毁。

实现线程安全的单例模式

  1. 饿汉模式(包括在static变量上new,或在static代码块中),加载类时初始化,即可把实例创建在方法区。
public class MyObject {
    // 在static变量上new
    private static MyObject myObject = new MyObject();

    /**
     * 在static代码块中
     */
    // static {
    //    myObject = new MyObject();
    // }

    private MyObject() {}

    public static MyObject getInstance() {
        return myObject;
    }
}
  1. 懒汉模式,使用DCL(双检查机制)实现。
public class MyObject {
    // 此处的volatile并不是用于可见性,而是为了防止指令重排序
    private volatile static MyObject myObject;

    private MyObject() {}

    public static MyObject getInstance() {
        try {
            if (null == myObject) {
                synchronized (MyObject.class) {
                    // 双重检查,保证创建对象并释放后,其他线程取得锁不会再次创建对象
                    if (null == myObject) {
                        // new不是原子操作,它包含了三条指令
                        // 如果变量不是volatile的,则其他线程可能会在第一个if出现myObject != null的情况
                        myObject = new MyObject;
                    }
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return myObject;
    }
}
  1. 静态内置类实现,与饿汉类似。
public class MyObject {
    // 静态内部类
    private static class MyObjectHandler {
        private static MyObject myObject = new MyObject;
    }

    private MyObject() {}

    public static MyObject getInstance() {
        return MyObjectHandler.myObject;
    }
}
  1. 使用Enum实现单例
public Enum MyObject {
    INSTANCE;
    private OthObject othObject;
    private MyObject() {
        othObject = new OthObject();
    }

    public OthObject getOthObject() {
        return othObject;
    }
}

// 使用
MyObject.INSTANCE.getOthObject()即可;

线程的状态

此处非线程生命周期,而是使用thread.getstate()方法,返回的枚举类型。

  1. NEW
  2. RUNNABLE
  3. BLICKED 线程在等待锁的时候
  4. WAITING 无期限的等待,例如wait()、await()
  5. TIMED_WAITING 指定时间的等待
  6. TERMINATED

线程异常处理

通过设置**setUncaughtExceptionHandler()**重写异常的处理方式

thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread t, Throwable e) {

    }
})

对于线程组异常,通常可以继承ThreadGroup类,重写uncaughtException(Thread t, Throwable e)方法,然后使用自定义的ThreadGroup线程组。

参考 《Java多线程编程核心技术》 高洪岩

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值