并发编程(一):初识多线程及线程通信

一,线程三种启动方式

    1,Thread方式

package com.gupao.concurrent;

/**
 * @author pj_zhang
 * @create 2019-09-24 21:06
 **/
public class StartThread extends Thread {

    @Override
    public void run() {
        System.out.println("Thread方式执行线程。。。");
    }

    public static void main(String[] args) {
        // 普通方式执行
        new StartThread().start();
        // lamdo表达式执行
        new Thread(() -> {
            System.out.println("lamdo: Thread方式执行线程。。。");
        }).start();
    }
}

    2,Runnable方式

package com.gupao.concurrent;

/**
 * @author pj_zhang
 * @create 2019-09-24 21:12
 **/
public class StartRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable方式执行线程。。。");
    }

    public static void main(String[] args) {
        new Thread(new StartRunnable()).start();
    }
}

    3,Callable方式 -- 带返回值

        * 不同于Thread和Runnable方式,Callable执行线程后可以返回线程执行结果

        * 通过返回结果Funture去get()结果集时,会阻塞直到结果返回

package com.gupao.concurrent;

import java.util.concurrent.*;

/**
 * @author pj_zhang
 * @create 2019-09-24 21:13
 **/
public class StartCallable implements Callable {
    @Override
    public Object call() throws Exception {
        TimeUnit.SECONDS.sleep(3);
        return "Callable方式执行线程";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService service = new ThreadPoolExecutor(
                20, 20, 0L,
                TimeUnit.SECONDS, new LinkedBlockingQueue<>());
        Long startTime;
        Long endTime;
        System.out.println("开始执行时间:" + (startTime = System.currentTimeMillis()));
        Future future = service.submit(new StartCallable());
        String result = (String) future.get();
        System.out.println("执行结束时间" +  (endTime = System.currentTimeMillis()));
        System.out.println(result + ", cast: " + (endTime - startTime));
    }
}

二,线程声明周期(六种线程状态)

    1,线程状态

        * NEW(线程初始化)

        * RUNNABLE(线程运行)

            a,Ready(就绪)

            b,Running(运行)

            c,NEW -(start())-> RUNNABLE

            d,Ready -线程调度-> Running

            e,Running -(yield()/线程调度)-> Ready

        * TERMINATED(线程销毁)

        * BLOKED(线程阻塞状态,锁抢占)

             a,synchorized加锁

        * WAITING(线程等待)

             a,wait()

             b,join()

             c,LockSupport.park()

        * TIME_WAITING(线程限时等待)

             a,wait(Long)

             b,sleep(Long)

    2,线程状态转换演示_代码

package com.gupao.concurrent;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author pj_zhang
 * @create 2019-09-24 21:42
 **/
public class ThreadStatusTest {

    public static void main(String[] args) {
        // 运行时线程
        new Thread(() -> {
            for (;;) {}
        }, "THREAD_RUNNING").start();
        // 等待线程
        new Thread(() -> {
            try {
                Object object = new Object();
                synchronized (object) {
                    object.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "THREAD_WAITING").start();
        // 定时等待线程
        new Thread(() -> {
            try {
                Object object = new Object();
                synchronized (object) {
                    object.wait(60 * 60 * 1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "TIME_WAITING_THREAD").start();
        Lock lock = new ReentrantLock();
        // 未阻塞线程
        new Thread(() -> {
            synchronized (lock) {
                for (; ; ) {}
            }
        }, "NOT_BLOCKING_THREAD").start();
        // 阻塞线程
        new Thread(() -> {
            try {
                Thread.sleep(1000);
                synchronized (lock) {
                    for (;;) {}
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "BLOCKING_THREAD").start();
    }
}

    3,线程状态转换演示_状态查看

        * 通过 jps 命令查看正在执行的线程PID

并发编程(一):初识多线程及线程通信_第1张图片

        * 通过 jstack pid 命令,查看正在执行的所有线程及其状态

        * 从中可以依次看到定义中的几种线程状态

三,线程中断

    1,线程中断:thread.interrupt()

    2,判断线程是否中断:Thread.currentThread().isInterrupted()

    3,线程中断代码演示;如下代码,interrupt()提供了一种可控的方式对线程进行中断,执行中断操作后,被中断线程可以根据当前业务运行情况在对应安全点对线程中断操作进行响应

package com.gupao.concurrent;

/**
 * @author pj_zhang
 * @create 2019-09-24 22:33
 **/
public class ThreadInterruptTest {

    private static int i = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
                // 线程初始化后,默认中断状态为false
                // 等其他线程对当前线程执行中断操作后,线程状态变为true
                for (; !Thread.currentThread().isInterrupted(); ) {
                    i++;
                }
                System.out.println(i);
                // 打印当前线程中断状态
                System.out.println(Thread.currentThread().isInterrupted());
            });
        thread.start();
        // 主线程睡眠一秒后,对线程进行中断操作
        Thread.sleep(1000);
        thread.interrupt();
    }
}

    4,线程等待中的线程中断演示,会抛出 InterruptedException 异常,(异常后会对线程中断状态进行复位,在下一部分解释)

package com.gupao.concurrent;

/**
 * @author pj_zhang
 * @create 2019-09-24 22:33
 **/
public class ThreadWaitingInterruptTest {

    private static int i = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            try {
                // 线程等待十秒,主线程1秒后中断,则必定触发中断异常
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                // 异常后, 线程会自动复位
                // 主线程调用interrupt后,线程中断状态从false -> true
                // 由于触发异常,则对线程中断重新赋值为初始状态false
                System.out.println("响应线程中断。。。,线程中断状态:"
                        + Thread.currentThread().isInterrupted());
            }
        });
        thread.start();
        // 主线程睡眠一秒后,对线程进行中断操作
        Thread.sleep(1000);
        thread.interrupt();
    }
}

四,线程复位

    1,线程中断是给执行一个中断信号,告诉执行线程已经进行了线程中断操作,具体后续操作由执行线程进行后续处理,执行线程在接收到线程中断信号后,可以对线程中断状态进行复位;

    2,线程复位两种方式

        a,API复位:Thread.interrupted()

package com.gupao.concurrent;

/**
 * @author pj_zhang
 * @create 2019-09-24 22:33
 **/
public class ThreadInterruptTest {

    private static int i = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
                // 线程初始化后,默认中断状态为false
                // 等其他线程对当前线程执行中断操作后,线程状态变为true
                for (; !Thread.currentThread().isInterrupted(); ) {
                    i++;
                }
                System.out.println(i);
                // 线程复位前中断状态
                System.out.println("before: " + Thread.currentThread().isInterrupted());
                // 线程复位
                Thread.interrupted();
                // 线程复位后中断状态
                System.out.println("after:" + Thread.currentThread().isInterrupted());
            });
        thread.start();
        // 主线程睡眠一秒后,对线程进行中断操作
        Thread.sleep(1000);
        thread.interrupt();
    }
}

并发编程(一):初识多线程及线程通信_第2张图片

        b,异常复位:等待中的线程接收到异常信号后,会抛出 InterruptedException 异常,捕获异常后线程状态已经由 JVM 复位,可继续进行后续处理,异常复位已经在上一部分演示;

五,线程通信

    1,线程等待和线程唤醒:wait()/notify()

package com.gupao.concurrent;

/**
 * @author pj_zhang
 * @create 2019-09-24 23:04
 **/
public class ThreadTelTest extends Thread {

    // 定义初始数量
    private static int i = 100;

    // 定义初始标志位
    // 此处模拟两道线程运行,通过简单标志位实现
    // 如果确实需要通过某种方式实现线程顺序执行,可以通过分布式锁实现
    // 参考之前博文中的zookeeper和redis分布式锁
    private static boolean flag = false;

    // 定义同步锁对象
    private static Object lock = new Object();

    public static void main(String[] args) {
        // 两道线程同时执行
        new Thread(new ThreadTelTest(), "售票员A").start();
        new Thread(new ThreadTelTest(), "售票员B").start();
    }

    @Override
    public void run() {
        try {
            for (;;) {
                synchronized (lock) {
                    if (i > 0) {
                        // flag为true,说明已经存在线程执行过,
                        // 当前线程为新线程或者被唤醒线程,且执行过的线程目前处于等待中
                        if (flag) {
                            System.out.println(Thread.currentThread().getName() + "卖出第" + i-- + "张票");
                            // 线程执行完成后,唤醒处于等待中线程
                            lock.notify();
                            // 当前线程等待,并释放锁对象,被唤醒的线程会直接抢占锁对象
                            lock.wait();
                        } else {
                            // flag为false,说明当前线程第一次进入,执行完业务代码后,修改flag表示为true
                            System.out.println(Thread.currentThread().getName() + "卖出第" + i-- + "张票");
                            flag = true;
                            // 线程等待,实现两道线程交替执行,因为是第一个进入的线程,不需要唤醒线程
                            lock.wait();
                        }
                    } else {
                        System.out.println("卖完了");
                        break;
                    }
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

    2,线程排队:join()

        * 线程排队执行代码演示

package com.gupao.concurrent;

/**
 * @author pj_zhang
 * @create 2019-09-24 22:52
 **/
public class ThreadJoinTest {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                // 通过不规则定时,演示线程随机执行
                Thread.sleep(200);
                System.out.println("线程一执行结束。。。");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(20);
                System.out.println("线程二执行结束。。。");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread t3 = new Thread(() -> {
            System.out.println("线程三执行结束。。。");
        });
        // 通过 join 方法,保证线程排队执行
        t1.start();
        t1.join();
        t2.start();
        t2.join();
        t3.start();
        t3.join();
    }
}

        * join()方法内部;

public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            // 从这一步可以看到,如果当前线程依旧存活,则阻塞主线程
            // 通过自旋一直等待直到线程执行结束
            while (isAlive()) {
                wait(0);
            }
        } else {
            // 这一部分表示限时阻塞
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

        * 从方法源码看,join()通过对主线程的阻塞实现线程方法的顺序执行,如果在上一步直接start()三道线程,再对线程逐一进行join(),则线程三会瞬间抢占时间片并执行,从而乱序,如图

并发编程(一):初识多线程及线程通信_第3张图片

你可能感兴趣的:(并发编程,Java,并发编程,多线程,线程通信)