0%

java并发编程基础

java并发编程基础

线程简介

为什么要使用线程

  • 更多的处理器核心
  • 更快的响应时间
  • 更好的编程模型

线程优先级

在java中,通过整型变量priority来控制优先级,范围是1~10。默认是5级,设置优先级时,针对频繁阻塞(休眠/IO操作)需要设置高优先级,偏计算的设置低优先级。(但是操作系统可能会忽略java优先级的设置)

线程的状态

线程的运行生命周期中有6种状态:

  • NEW:初始状态,线程被构建,但是没有调用start()方法
  • RUNNABLE:运行状态
  • BLOCKED:阻塞状态表示线程阻塞于锁
  • WAITING:等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定的操作(通知或中断)
  • TIME_WAITING:超时等待状态,该状态不同于WAITING,他是可以在指定的时间自动返回的。
  • TERMINATED:终止状态,表示当前线程已经执行完毕。
public static void main(String[] args) {

        new Thread(new TimeWaiting(), "TimeWaitingThread").start();
        new Thread(new Waiting(), "WaitingThread").start();
        new Thread(new Blocked(),"BlockedThread-1").start();
        new Thread(new Blocked(),"BlockedThread-2").start();

    }

    // 该线程不断的进行睡眠
    static class TimeWaiting implements Runnable {
        @Override
        public void run() {

            while (true) {
                SleepUtils.second(100);
            }
        }
    }

    // 该线程在Waiting.class实例上等待
    static class Waiting implements Runnable {
        @Override
        public void run() {
            while (true) {
                synchronized (Waiting.class) {
                    try {
                        Waiting.class.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        }
    }

    // 该线程在Blocked.class 实例上加锁后,不会释放该锁
    static class Blocked implements Runnable {
        @Override
        public void run() {
            synchronized (Blocked.class) {
                while ((true)) {
                    SleepUtils.second(100);
                }
            }
        }
    }



    public static class SleepUtils {
        public static final void second(long seconds) {
            try {
                TimeUnit.SECONDS.sleep(seconds);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }    

通过运行这段代码,在输入jps找到pid,再执行jstack $pid,我们发现

// BlockedThread-2线程阻塞在获取Blocked.class示例的锁上
"BlockedThread-2" prio=5 tid=0x00007feacb05d000 nid=0x5d03 waiting for monitor
entry [0x000000010fd58000]
java.lang.Thread.State: BLOCKED (on object monitor)
// BlockedThread-1线程获取到了Blocked.class的锁
"BlockedThread-1" prio=5 tid=0x00007feacb05a000 nid=0x5b03 waiting on condition
[0x000000010fc55000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
// WaitingThread线程在Waiting实例上等待
"WaitingThread" prio=5 tid=0x00007feacb059800 nid=0x5903 in Object.wait()
[0x000000010fb52000]
java.lang.Thread.State: WAITING (on object monitor)
// TimeWaitingThread线程处于超时等待
"TimeWaitingThread" prio=5 tid=0x00007feacb058800 nid=0x5703 waiting on condition
[0x000000010fa4f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)

启动和终止线程

构造线程对象

初始化一个Thead,内部执行的是init()方法

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;

        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

启动线程-start()方法

start()方法的含义是:当前线程同步告知java虚拟机,只要线程规划期空闲,应立即启动调用start()的线程

理解中断

其他线程通过调用该线程的interrupt()来对其进行中断操作

线程间的通信

volatile关键字

volatile用来修饰字段,就是告知程序任何对该变量的访问均需要从共享内存中获取,对他的改变必须同步刷新共享内存,他能保证所有线程对变量的访问的可见性。

synchronized关键字

synchronized可以修饰方法或者以同步块的形式来进行使用。它主要确保多个线程在同一时刻,只能有一个线程处于方法或者同步块中,保证了线程对变量的访问的可见性于排他性。

任何对象都有自己的监视器,当这个对象被同步块或者同步方法调用时,执行方法必须先获取该对象的监视器,而没有获取到监视器的线程则会被阻塞到同步块和同步方法的入口,进入BLOCKED状态。

等待/通知机制

相关的方法

方法名称 描述
notify 通知一个在对象上等待的线程,使其从wait()方法返回,而返回的前提是该线程获取到了对象的锁
notifyAll 通知所有等待在该对象的线程
wait 调用该方法进入waiting状态,只有等待其他线程通知或被中断才能返回,调用wait()方法,会释放对象的锁
wait(long) 超时等待一段时间,没有通知就超时返回
wait(long,int) 更细粒度的控制

经典范式,等待/通知的经典范式,分为两部分,等待方/通知方

等待方

  • 获取对象的锁

  • 如果条件不满足,那么调用对象的wait()方法,被统治后仍要检查条件

  • 条件满足则执行对应的逻辑

  • synchronized(对象) {
        while(条件不满足) {
            对象.wait();
        }
        对应的逻辑处理
    }

通知方

  • 获取对象的锁

  • 改变条件

  • 通知所有的等待在该对象的线程

  • synchronized(对象) {
        改变条件
        对象.notifyAll();
    }

Thread.join()的使用

thread.join()指的是当前线程等待thead线程终止后才从thread.join()线程返回。

ThreadLocal的使用

ThreadLocal线程变量,是一个以ThreadLocal对象为键,任意对象为值的存储对象。这个结构被附带到线程上。

private static final ThreadLocal<Long> TIME_THREAD_LOCAL = new ThreadLocal<>();
    public static void begin() {
        TIME_THREAD_LOCAL.set(System.currentTimeMillis());
    }

    public static long end() {
        return System.currentTimeMillis() - TIME_THREAD_LOCAL.get();
    }

    public static void main(String[] args) throws InterruptedException {

        Profiler.begin();

        TimeUnit.SECONDS.sleep(2);

        TimeUnit.SECONDS.sleep(20);

        System.out.println("cost:" + Profiler.end() + "mills");
    }

begin()方法将时间绑定到当前线程上,end()方法返回的是begin()方法到end()方法调用的时间差

~~