• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

Java多线程编程核心技术

互联网 diligentman 1周前 (11-22) 6次浏览

停止线程

终止正在运行的线程:

  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与synchronized的区别:

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

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

  1. 从内存取i
  2. +1计算
  3. 写回内存
    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) {
                    // 双重检查,保证创建对象并释放后,其他线程取得锁不会再次创建对象
                    // 使用volatile可以保证所有线程都从主内取该对象进行比较
                    if (null == myObject) {
                        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)