关于java:java-线程学习

33次阅读

共计 5113 个字符,预计需要花费 13 分钟才能阅读完成。

感觉本人 java 基础知识学的太差,记录一下本人学习 深入浅出多线程 的笔记。

笔记连贯:
https://flowus.cn/share/d4a68486-0162-4759-816e-551b275218f3
【FlowUs 息流】

1. 过程和线程基本概念

过程产生的背景:最后计算机只能输出一次指令而后执行一次,效率较低。起初有了 批处理零碎,用户能够将一串的指令交给计算机,由计算机顺次解决。然而还是串行解决,随程序的阻塞而阻塞。

为了解决这个问题,呈现了过程的概念:指正在运行的一个程序或应用程序,内存中能够存在多个过程。CPU 采纳工夫片轮转的形式运行过程。每个过程的工夫片完结后,把 CPU 调配给下一个过程,如果以后过程工作没完结,则保留以后过程的信息,期待下一次调配;

然而,人们并不满足于此。如果一个过程蕴含多个子工作,过程也只能一一执行,效率不高。

那么能不能让这些子工作同时执行呢?于是人们又提出了线程的概念,那么能不能让这些子工作同时执行呢?于是人们又提出了线程的概念,让一个线程执行一个子工作,这样一个过程就蕴含了多个线程,每个线程负责一个独自的子工作。
例如:一个下载软件能够应用多个线程同时下载多个文件,从而大大缩短下载工夫。

线程和过程:

2. Thread 类和 Runnable 接口

自定义 Thread 类:

public class Demo {
    public static class MyThread extends Thread {
        @Override
        public void run() {System.out.println("MyThread");
        }
    }

    public static void main(String[] args) {Thread myThread = new MyThread();
        myThread.start();}
}
  • start()办法后,该线程才算启动
  • 调用了 start()办法后,虚构机会创立一个线程,而后等到这个线程第一次失去工夫片时再调用 run()办法。
  • 第二次调用 start()办法会抛出 IllegalThreadStateException 异样。、

Thread 类是一个 Runnable 接口的实现类。

@FunctionalInterface
public interface Runnable {public abstract void run();
}

Runnable 接口只有一个 run 办法,并且有 @FunctionalInterface 注解。意思是:函数式接口, 接口外面只能有一个形象办法

阐明咱们能够应用 Lambda 表达式

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

        // Java 8 函数式编程,能够省略 MyThread 类
        new Thread(() -> {System.out.println("Java 8 匿名外部类");
        }).start();}

2.1 线程优先级

  • 每个 Java 程序都有一个默认的主线程,就是通过 JVM 启动的第一个线程 main 线程。
  • Java 中线程优先级能够指定,范畴是 1~10。Java 默认的线程优先级为 5,
  • Java 中的优先级来说不是特地的牢靠,Java 程序中对线程所设置的优先级只是给操作系统一个倡议,操作系统不肯定会驳回。而真正的调用程序,是由操作系统的线程调度算法决定的

例如咱们能够应用如下的代码测试一下:

    public static class T1 extends Thread {
        @Override
        public void run() {super.run();
            System.out.println(String.format("以后执行的线程是:%s,优先级:%d",
                    Thread.currentThread().getName(),
                    Thread.currentThread().getPriority()));
        }
    }

    @Test
    void contextLoads() throws ExecutionException, InterruptedException {IntStream.range(1, 10).forEach(i -> {Thread thread = new Thread(new T1());
            thread.setPriority(i);
            thread.start();});
    }

能够看到不齐全依照优先级来,然而大抵差不多。

3. 线程组(ThreadGroup)

Java 中用 ThreadGroup 来示意线程组,咱们能够应用线程组对线程进行批量管制。

ThreadGroup 是一个规范的向下援用的树状构造。

顶层的 ThreadGroup 被称为零碎线程组(system thread group),由 JVM 运行时创立并领有它。

除了零碎线程组外,每个 ThreadGroup 对象都有一个父 ThreadGroup,即间接蕴含它的那个 ThreadGroup。

为什么这么设计呢?

节俭资源:依据树形构造,能够很容易递归回收该 ThreadGroup 中的线程或线程组,防止了内存透露和资源节约等问题。

确保线程平安:通过 ThreadGroup 能够限度某些线程的运行权限,比方能够设置特定的权限来避免线程意外获得零碎特权,确保线程不会危及系统安全。能够通过重写 ThreadGroup 的 checkAccess 办法来实现限度某些线程的运行权限。

不便监控和调试:通过 ThreadGroup 能够不便地查看和操纵整个线程组中的所有线程,从而便于进行监控和调试。

例如通过上面的办法就能够获取以后线程组运行的所有线程:

IntStream.range(1, 10).forEach(i -> {Thread thread = new Thread(() -> {
                try {Thread.sleep(1000L);
                } catch (InterruptedException e) {throw new RuntimeException(e);
                }
            });
            thread.start();});
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();

Thread[] threads = new Thread[threadGroup.activeCount()];

threadGroup.enumerate(threads);

后果:

4. Java 线程的状态

在当初的操作系统中,线程是被视为轻量级过程的,所以操作系统线程的状态其实和操作系统过程的状态比拟类似的。

4.1 Java 线程的 6 个状态

// Thread.State 源码
public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;
}

4.1.1 NEW

文档中含意为:Thread state for a thread which has not yet started. 示意尚未启动的线程

Thread thread = new Thread(() -> {});
System.out.println(thread.getState()); // 输入 NEW 

当初有两个问题:

  1. 重复调用同一个线程的 start()办法是否可行?
  2. 如果一个线程执行结束(此时处于 TERMINATED 状态),再次调用这个线程的 start()办法是否可行?

从源码剖析:

 public synchronized void start() {if (threadStatus != 0)
            throw new IllegalThreadStateException();

        group.add(this);

        boolean started = false;
        try {start0();
            started = true;
        } finally {
            try {if (!started) {group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

从 start0()看不出任何对 state 的操作。咱们只能从 threadStatus 动手

能够看到 threadStatus 的变量。如果它不等于 0,调用 start()会间接抛出异样的。

上面用代码测试一下:

@Test
public void testStartMethod() {Thread thread = new Thread(() -> {}, "myThread");
    thread.start(); // 第一次调用
    thread.start(); // 第二次调用}

第一次:

第二次:

而后面说到:如果它不等于 0,调用 start()会间接抛出异样的。故答案是否,不能 start 两次


那第二个问题:如果一个线程执行结束(此时处于 TERMINATED 状态),再次调用这个线程的 start()办法是否可行?

从获取线程状态源码剖析:

    // 返回线程的状态
    public State getState() {
        // get current thread state
        return sun.misc.VM.toThreadState(threadStatus);
    }
    
     public static Thread.State toThreadState(int var0) {if ((var0 & 4) != 0) {return State.RUNNABLE;} else if ((var0 & 1024) != 0) {return State.BLOCKED;} else if ((var0 & 16) != 0) {return State.WAITING;} else if ((var0 & 32) != 0) {return State.TIMED_WAITING;} else if ((var0 & 2) != 0) {return State.TERMINATED;} else {return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE;
        }
    }

获取线程的状态是通过 toThreadState(int var0)函数获取的,参数就是 threadStatus

通过 var0和 数字按位运算失去后果。

如果 (var0 & 4) != 0 成立,示意 var0 的第 3 位(从右往左数,从 0 开始计数)是 1,即示意线程状态为 RUNNABLE;如果 (var0 & 1024) != 0 成立,示意 var0 的第 11 位是 1,即示意线程状态为 BLOCKED。

回到方才的问题,当线程处于 TERMINATED 状态,阐明 var0 的第 3 位为 0,即 threadStatus ! = 0

从之前的代码就能够看出会抛出 IllegalThreadStateException 异样。故答案也是否

4.1.2 RUNNABLE

Java 线程的 RUNNABLE 状态其实是包含了传统操作系统线程的 readyrunning两个状态。

4.1.3 BLOCKED

阻塞状态。处于 BLOCKED 状态的线程正等待锁的开释以进入同步区。

能够类比为一个人正在应用一部电话,而另一个人也想要应用电话。因为电话只有一个,因而另一个人必须期待以后使用者完结后能力应用。在此期间,他就处于 blocked 状态。因为他晓得那部电话曾经被占用了,不能立刻应用。

4.1.4 WAITING

期待状态。处于期待状态的线程变成 RUNNABLE 状态须要其余线程唤醒。

调用如下 3 个办法会使线程进入期待状态:

  • Object.wait():使以后线程处于期待状态直到另一个线程唤醒它;
  • Thread.join():期待线程执行结束,底层调用的是 Object 实例的 wait 办法;
  • LockSupport.park():除非取得调用许可,否则禁用以后线程进行线程调度。

4.1.5 TIMED_WAITING

超时期待状态。线程期待一个具体的工夫,工夫到后会被主动唤醒。主动唤醒后,领有了去抢夺锁的资格。

调用如下办法会使线程进入超时期待状态:

  • Thread.sleep(long millis):使以后线程睡眠指定工夫;
  • Object.wait(long timeout):线程休眠指定工夫,期待期间能够通过 notify()/notifyAll()唤醒;
  • Thread.join(long millis):期待以后线程最多执行 millis 毫秒,如果 millis 为 0,则会始终执行;
  • LockSupport.parkNanos(long nanos):除非取得调用许可,否则禁用以后线程进行线程调度指定工夫;
  • LockSupport.parkUntil(long deadline):同上,也是禁止线程进行调度指定工夫;

4.1.6 TERMINATED

终止状态。此时线程已执行结束。

正文完
 0