并发之线程

线程创建

  1. 继承 Thread 类,本质是 Runable 的实现类
  2. 实现 Runnable 接口,重写 run() 方法
    • 无返回值
    • 无法将异常抛到外面,任何check Exception都需要在代码块中自行解决
    • 异步执行,不阻塞主方法
  3. 实现 Callable 接口,重写 call() 方法
    • 有返回值
    • 可以将异常抛到外层
以下为上三种类型代码实现
public class TestDemo {

    public static void main(String[] args) {
        // 1. 继承Thread
        ThreadDemo threadDemo = new ThreadDemo();
        threadDemo.start(); // 异步执行
        // threadDemo.run(); // 同步执行
        // 2. 实现 Runnable
        RunnerDemo runnerDemo = new RunnerDemo();
        Thread runnableThread = new Thread(runnerDemo);
        runnableThread.start();
        // 2.1 另一种简单写法
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+":RunnerDemo2 简写方式");
        }).start();
        // 3. 实现 Callable
        CallDemo callDemo = new CallDemo();
        FutureTask futureTask = new FutureTask(callDemo);
        new Thread(futureTask).start();
        // 3.1 简单写法
        FutureTask<String> task = new FutureTask<>(() -> {
            System.out.println(Thread.currentThread().getName() + ":CallDemo2 简单写法");
            return "CallDemo2";
        });
        new Thread(task).start();
        try {
            // 阻塞并等待获取结果,本质也是 runnable
            String result = task.get();
            System.out.println("call 执行返回结果:"+ result);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }

    }
}

// 1.继承 Thread 重写 run方法
class ThreadDemo extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+":ThreadDemo");
    }
}
// 2. 实现 Runnable 接口,实现 run方法
class RunnerDemo implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+":RunnerDemo1");
    }
}
// 3. 实现 Callable 接口,实现 call方法
class CallDemo implements Callable<String> {

    @Override
    public String call() {
        System.out.println(Thread.currentThread().getName()+":CallDemo1");
        return "CallDemo1";
    }
}


D:\JDK8\bin\java.exe "-javaagent:D:\IntelliJ IDEA
Thread-0:ThreadDemo
Thread-1:RunnerDemo1
Thread-2:RunnerDemo2 简写方式
Thread-3:CallDemo1
Thread-4:CallDemo2 简单写法
call 执行返回结果:CallDemo2

Process finished with exit code 0

常用方法

  • sleep()
    意思就是睡眠。让当前线程放弃资源,让给其它线程去执行。睡眠时间由你来定,时间一到重新去争夺资源。
  • yield()
    意思 放弃资源。让当前线程放弃资源,然后与其它线程重新争夺资源。可能重新抢到,可能其它线程抢到。
  • join()
    意思 加入其它线程。停止当前线程 t1, 执行指定线程t2,t2 执行完再执行t1。用于顺序执行。
    自己 join 自己,没有意义

线程状态

线程状态:

  • 新建状态
  • 就绪状态(代码中没有此状态,可以认为是 运行状态的前置状态)
  • 等待状态
  • 超时等待状态
  • 阻塞状态
  • 运行状态
  • 结束状态

备注:
BOLCKED、WAITING、TIME_WAITING 三种状态本质上都是放弃资源。

  • BOLCKED 当线程阻塞(synchronized)后才会进行此状态
  • WAITING 实际上调用USafe.pack()方法,JUC包下的类挂起时会进行此状态
  • TIME_WAITING 调用USafe.pack(time)方法,进入等待,直到被唤醒

三种类型 都是线程挂起

image

  1. NEW一个线程,在没有调用 start()前,线程处于新建状态
  2. 调用start()方法后,存在两个状态
    1. 线程获取到资源,线程处于运行状态。(之后,调用 yield方法,状态由运行到就绪
    2. 线程没有获取到资源,线程会进行等待队列,处于`就绪状态。(之后,获取资源后,状态由就绪到运行
  3. 运行情况下的状态变迁
    • 当线程没有获取到锁情况下,进行阻塞队列,处于阻塞状态
    • 获取到锁后,线程状态由阻塞状态转为就绪状态
    • 调用 thread.sleep() 方法后,进行等待状态。(使用 notify()方法,线程状态由等待 到 运行
  4. 运行结束,状态由运行到结束

Join 实现原理

Join方法的本质是基于synchronized以及wait与nofity实现的。直接对当前线程对象加锁,然后wait挂起线程,wait判断的逻辑是线程是否存活。isAlive()。

// join 方法功能就是,告诉线程我们要释放资源了
public final synchronized void join(final long millis)
    throws InterruptedException {
        if (millis > 0) {
            if (isAlive()) {
                final long startTime = System.nanoTime();
                long delay = millis;
                do {
				// 等待 delay时间后,才会执行 wait方法
                    wait(delay);
                } while (isAlive() && (delay = millis -
                        TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
            }
        } else if (millis == 0) {
		// 如果线程存活,则返回 true,否则就是 false
            while (isAlive()) {
			//wait 是优雅的关闭资源,需要等待 run()/call() 方法完成后,才会释放资源。未释放前,线程等待(重点)
			// 而 synchronized的存在,使外围主线程等待。
                wait(0);
            }
        } else {
            throw new IllegalArgumentException("timeout value is negative");
        }
    }

Stop() 方法问题

不赞同使用此方法。
stop()方法会强制停止run()/call()方法的执行,存在代码未执行的情况。

中断 interrupt

中断的也是和线程停止有关,线程A想停止线程B的运行。整个流程如下:

  1. 线程A中调用线程B的interrupt方法设置为 true
  2. 在线程B需要执行时,run方法中手写 interrupt方法的判断,并执行后续操作
  3. 如果线程B处于sleep的状态,会唤醒线程B并抛出异常 interruptexeption。此时可以通过捕获异常来执行后续逻辑

主线程凉凉,子线程怎样

  1. 如果子线程是用户线程,则没有影响
  2. 如果字线程是守护线程,则一起凉凉
posted @ 2022-06-15 11:50  之士咖啡  阅读(18)  评论(0编辑  收藏  举报