Java线程

线程

线程和进程

进程是操作系统中的一个独立执行单元,它包含了程序代码、数据和系统资源的副本。每个进程都有自己的地址空间,它们之间通常是相互隔离的。

线程是进程内的一个执行单元,多个线程共享同一个进程的地址空间和资源。一个进程可以包含多个线程。

进程是程序运行资源分配(内存)的最小单位;线程是CPU调度的最小单位。

线程的状态

public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * 
    *
  • {@link Object#wait() Object.wait} with no timeout
  • *
  • {@link #join() Thread.join} with no timeout
  • *
  • {@link LockSupport#park() LockSupport.park}
  • *
* *

A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called Object.wait() * on an object is waiting for another thread to call * Object.notify() or Object.notifyAll() on * that object. A thread that has called Thread.join() * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: *

    *
  • {@link #sleep Thread.sleep}
  • *
  • {@link Object#wait(long) Object.wait} with timeout
  • *
  • {@link #join(long) Thread.join} with timeout
  • *
  • {@link LockSupport#parkNanos LockSupport.parkNanos}
  • *
  • {@link LockSupport#parkUntil LockSupport.parkUntil}
  • *
*/
TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }
  • 新建状态:

    使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

  • 就绪状态:

    当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

  • 运行状态:

    如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

  • 阻塞状态:

    如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

    • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
    • 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
    • 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
  • 死亡状态:

    一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

线程的生命周期

Java线程_第1张图片

如何创建一个线程?

Java 提供了四种创建线程的方法:

  • 通过继承 Thread 类本身
  • 通过实现 Runnable 接口
  • 通过 Callable 和 Future 创建线程
  • 线程池

show me the code!

继承Thread

package com.junfeng.manage.test;

public class MyThread extends Thread {

    public static void main(String[] args) {
        System.out.println("main----start");
        MyThread thread01 = new MyThread();
        thread01.start();
        System.out.println("main----stop");
    }

    @Override
    public void run() {
        System.out.println("junfeng");
    }

}

实现Runnable

package com.junfeng.manage.test;

public class RunnableTest implements Runnable{

    public static void main(String[] args) {
        System.out.println("main----start");
        new Thread(new RunnableTest()).start();
        System.out.println("main----stop");
    }

    @Override
    public void run() {
        System.out.println("hello,junfeng~");
    }

}

Callable和Future

package com.junfeng.manage.test;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class MyCallable implements Callable<String > {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main----start");
        FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
        new Thread(futureTask).start();
        System.out.println(futureTask.get());
        System.out.println("main----stop");
    }


    @Override
    public String call() throws Exception {
        return "hello,junfeng~";
    }
}

线程池

package com.junfeng.manage.test;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolTest {
    static ExecutorService service = Executors.newFixedThreadPool(2);//实例创建的为使用的固定线程的线程池

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main----start");
        //传入一个Runable对象
        service.execute(new MyRunnable());
        //关闭线程池
        service.shutdown();
        System.out.println("main----stop");
    }


}

线程顺序执行

假如有3个线程(A,B,C),怎么让它们按照指定的顺序执行任务呢?假设希望按照 A->B->C 的顺序执行。

1、thread.join()

2、SingleThreadExecutor(单线程池)

3、Semaphore(信号量)

利用Semaphore、CountDownLatch、CyclicBarrier 当成信号变量,作顺序开关。这里只写Semaphore代码,CountDownLatch、CyclicBarrier是类似的。注意:这里需要两个信号通知,因此需要定义两个Semaphore。

@Slf4j
public class Test {
 
    /**
     * Semaphore 信号量实现
     * {@link Semaphore}
     */
    public static void main(String[] args) {
        // 3个线程需要2两个信号量,A->B需要一个,B->C需要一个
        Semaphore signalAB = new Semaphore(0);
        Semaphore signalBC = new Semaphore(0);
 
        A a = new A(signalAB);
        B b = new B(signalAB, signalBC);
        C c = new C(signalBC);
 
        // 线程池中 按顺序执行 A->B->C
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        executorService.execute(b);
        executorService.execute(c);
        executorService.execute(a);
 
        // 停止接受新任务,当已有任务将执行完,关闭线程池
        executorService.shutdown();
        while (!executorService.isTerminated()) {
            // 等待所有线程执行完成
        }
        System.out.println("over");
        System.exit(0);
 
    }
 
    @AllArgsConstructor
    static class A implements Runnable {
        private Semaphore semaphoreAB;
 
        @SneakyThrows
        @Override
        public void run() {
            log.info("A");
            semaphoreAB.release();
        }
    }
 
    @AllArgsConstructor
    static class B implements Runnable {
        private Semaphore semaphoreAB;
        private Semaphore semaphoreBC;
 
        @SneakyThrows
        @Override
        public void run() {
            // semaphore 信号量-1,总数为0的时候会等待
            semaphoreAB.acquire();
            log.info("B");
            // semaphore 信号量+1
            semaphoreBC.release();
        }
    }
 
    @AllArgsConstructor
    static class C implements Runnable {
        private Semaphore semaphoreBC;
 
        @SneakyThrows
        @Override
        public void run() {
            semaphoreBC.acquire();
            log.info("C");
        }
    }
}

4、CompletableFuture

@Slf4j
public class ThreadABC {
 
    /**
     * CompletableFuture (推荐)
     * JDK1.8中 CompletableFuture提供了非常强大的Future的扩展功能,简化异步编程,
     * 提供函数式编程的能力,可帮助我们完成复杂的线程的阶段行编程(CompletionStage)
     * {@link java.util.concurrent.CompletableFuture}
     */
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
 
        // 有a,b,c三个线程(任务)
        Runnable a = () -> log.info("A");
        Runnable b = () -> log.info("B");
        Runnable c = () -> log.info("C");
 
        // 异步执行
        CompletableFuture.runAsync(a, executorService).thenRun(b).thenRun(c);
 
        log.info("main thread.");
        // 停止接受新任务,当已有任务将执行完,关闭线程池
        executorService.shutdown();
        while (!executorService.isTerminated()) {
            // 等待所有线程执行完成
        }
        System.exit(0);
    }
}

如何停止一个线程?

  1. stop
  2. interrupt
  3. 使用退出标识

sleep和wait的区别

  1. sleep进入阻塞状态,wait进入等待状态,调用notify进入就绪状态。
  2. sleep不会释放锁,wait会释放锁。
  3. sleep是Thread的方法,wait是Object类的方法。

start和run的区别

  • start()是Thread类方法,底层调用了native方法,run()是Runnable接口类的普通方法。
  • start()方法用来启动线程,实现了多线程运行;run()方法是普通方法,程序还是顺序执行。

守护线程

守护线程也就是“后台线程”,一般用来执行后台任务,守护线程最典型的应用就是GC(垃圾回收器)。

唯一的区别就是Java虚拟机在所有<用户线程>都结束后就会退出,而不会等<守护线程>执行完。

你可能感兴趣的:(java基础,java)