JUC(二):Java 线程预备知识

1. 创建和运行线程

方法一,直接使用 Thread

// 创建线程对象
Thread t = new Thread() {
    public void run() {
        // 要执行的任务
    }
};
// 启动线程
t.start();

例如:

// 构造方法的参数是给线程指定名字,推荐
Thread t1 = new Thread("t1") {
    @Override
    // run 方法内实现了要执行的任务
    public void run() {
        log.debug("hello");
    }
};
t1.start();

输出:

19:19:00 [t1] c.ThreadStarter - hello

方法二,使用 Runnable 配合 Thread

把【线程】和【任务】(要执行的代码)分开

  • Thread 代表线程
  • Runnable 可运行的任务(线程要执行的代码)
Runnable runnable = new Runnable() {
    public void run(){
        // 要执行的任务
    }
};
// 创建线程对象
Thread t = new Thread( runnable );
// 启动线程
t.start(); 

例如:

// 创建任务对象
Runnable task2 = new Runnable() {
    @Override
    public void run() {
        log.debug("hello");
    }
};
// 参数1 是任务对象; 参数2 是线程名字,推荐
Thread t2 = new Thread(task2, "t2");
t2.start();

输出:

9:19:00 [t2] c.ThreadStarter - hello

Java 8 以后可以使用 lambda 精简代码

// 创建任务对象
Runnable task2 = () -> log.debug("hello");
// 参数1 是任务对象; 参数2 是线程名字,推荐
Thread t2 = new Thread(task2, "t2");
t2.start();
Thread 与 Runnable 的关系

分析 Thread 的源码,理清它与 Runnable 的关系

//Runnable源码
public interface Runnable {
    public abstract void run();
}
//Thread源码(部分)
public class Thread implements Runnable {
    /* What will be run. */
    private Runnable target;
    
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        //...
        this.target = target;
       //...
    }
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
小结
  • 方法1 是把线程和任务合并在了一起,方法2 是把线程和任务分开了
  • 用 Runnable 更容易与线程池等高级API 配合
  • 用 Runnable 让任务类脱离了 Thread 继承体系,更灵活

方法三,FutureTask 配合 Thread

FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况

// 创建任务对象
FutureTask<Integer> task3 = new FutureTask<>(() -> {
 log.debug("hello");
 return 100;
});
// 参数1 是任务对象; 参数2 是线程名字,推荐
new Thread(task3, "t3").start();
// 主线程阻塞,同步等待 task 执行完毕的结果
Integer result = task3.get();
log.debug("结果是:{}", result);

输出

19:22:27 [t3] c.ThreadStarter - hello
19:22:27 [main] c.ThreadStarter - 结果是:100

源码分析

//FutureTask源码(部分)
public class FutureTask<V> implements RunnableFuture<V> {
    /** The underlying callable; nulled out after running */
    private Callable<V> callable;
    
    /** The result to return or exception to throw from get() */
    private Object outcome; // non-volatile, protected by state reads/writes
    
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
    
    public void run() {
       //...
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        }
        //...
    }
    
    protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }
    
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }
    
    private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }
}    
//Callable源码
@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

说明:

  • FutureTask内置了一个Callable对象,初始化方法将指定的Callable赋给这个对象。
  • FutureTask实现了Runnable接口,并重写了Run方法,在Run方法中调用了Callable中的call方法,并将返回值赋值给outcome变量
  • get方法就是取出outcome的值。

2 观察多个线程同时运行

主要是理解

  • 交替执行
  • 谁先谁后,不由我们控制

示例代码

@Slf4j(topic = "c.TestMultiThread")
public class TestMultiThread {

    public static void main(String[] args) {
        new Thread(() -> {
            while(true) {
                log.debug("running");
            }
        },"t1").start();
        new Thread(() -> {
            while(true) {
                log.debug("running");
            }
        },"t2").start();
    }
}

运行结果:

23:45:26.254 c.TestMultiThread [t2] - running
23:45:26.254 c.TestMultiThread [t2] - running
23:45:26.254 c.TestMultiThread [t2] - running
23:45:26.254 c.TestMultiThread [t2] - running
23:45:26.254 c.TestMultiThread [t1] - running
23:45:26.254 c.TestMultiThread [t1] - running
23:45:26.254 c.TestMultiThread [t1] - running
23:45:26.254 c.TestMultiThread [t1] - running
23:45:26.254 c.TestMultiThread [t1] - running
23:45:26.254 c.TestMultiThread [t1] - running

3. 查看进程线程的方法

windows

  • 任务管理器可以查看进程和线程数,也可以用来杀死进程
  • tasklist 查看进程
    • tasklist | findstr (查找关键字)
  • taskkill 杀死进程
    • taskkill /F(彻底杀死)/PID(进程PID)

Linux

  • ps -fe 查看所有进程

  • ps -fT -p 查看某个进程(PID)的所有线程

  • kill 杀死进程 top 按大写 H 切换是否显示线程

  • top -H -p 查看某个进程(PID)的所有线程

    • Java 进程的线程 全是Java没有线程名的解决

      来源于互联网收集,https://blog.csdn.net/ActionTech/article/details/120435414

      • 如果应用 Oracle jdk8 或者更早的 jdk ,那么还得通过 jstack 或者其余办法来对应线程号和逻辑线程名称。
      • 如果应用 Open jdk8,倡议降级到222之后,这样能够通过top命令间接看到线程的名称,放慢诊断。
      • 倡议利用在设置线程名时,尽量在15个字符内表白出惟一的含意,便于察看和剖析,当然,这一点 dble 做得不好,会在之后进行调整和批改
      • 当然,社区还有一些其余工具,比方阿里的 Arthas 应该也能实现线程 id 和名字对应的性能,不过引入第三方总是件麻烦的事件,还是原生的更香。
      [root@localhost home]# top -H -p 5861
      top - 11:01:42 up 55 min,  2 users,  load average: 0.18, 0.16, 0.15
      Threads:  19 total,   0 running,  19 sleeping,   0 stopped,   0 zombie
      %Cpu(s):  8.2 us,  4.9 sy,  0.0 ni, 86.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
      KiB Mem :  3861296 total,   111468 free,  3287268 used,   462560 buff/cache
      KiB Swap:  2097148 total,  1814680 free,   282468 used.   329880 avail Mem 
         PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND              
        5863 root      20   0 3622740  25452  12344 S  0.3  0.7   0:00.01 GC Thread#0        
        5861 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.00 java                 
        5862 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.05 java                
        5864 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.00 G1 Main Marker       
        5865 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.00 G1 Conc#0            
        5866 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.00 G1 Refine#0         
        5867 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.01 G1 Young RemSet    
        5868 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.01 VM Thread            
        5869 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.00 Reference Handl         
        5870 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.00 Finalizer               
        5871 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.00 Signal Dispatch         
        5872 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.00 Service Thread         
        5873 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.00 C2 CompilerThre          
        5874 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.01 C1 CompilerThre       
        5875 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.00 Sweeper thread           
        5876 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.09 VM Periodic Tas         
        5877 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.00 Common-Cleaner  
        5878 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.00 t                         
        5879 root      20   0 3622740  25452  12344 S  0.0  0.7   0:00.00 t1 
      

Java

  • jps 命令查看所有 Java 进程

  • jstack 查看某个 Java 进程(PID)的所有线程状态

    [root@localhost home]# jstack 4219
    2022-07-26 10:20:12
    Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.321-b07 mixed mode):
    
    "Attach Listener" #11 daemon prio=9 os_prio=0 tid=0x00007fe458001000 nid=0x10da waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "DestroyJavaVM" #10 prio=5 os_prio=0 tid=0x00007fe48c009800 nid=0x107c waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "t1" #9 prio=5 os_prio=0 tid=0x00007fe48c195000 nid=0x1088 waiting on condition [0x00007fe4775f4000]
       java.lang.Thread.State: TIMED_WAITING (sleeping)
    	at java.lang.Thread.sleep(Native Method)
    	at java.lang.Thread.sleep(Thread.java:342)
    	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
    	at RunPhenomenon.lambda$main$1(RunPhenomenon.java:31)
    	at RunPhenomenon$$Lambda$2/303563356.run(Unknown Source)
    	at java.lang.Thread.run(Thread.java:750)
    
    "t2" #8 prio=5 os_prio=0 tid=0x00007fe48c193800 nid=0x1087 waiting on condition [0x00007fe4776f5000]
       java.lang.Thread.State: TIMED_WAITING (sleeping)
    	at java.lang.Thread.sleep(Native Method)
    	at java.lang.Thread.sleep(Thread.java:342)
    	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
    	at RunPhenomenon.lambda$main$0(RunPhenomenon.java:20)
    	at RunPhenomenon$$Lambda$1/471910020.run(Unknown Source)
    	at java.lang.Thread.run(Thread.java:750)
    
    "Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007fe48c0dc800 nid=0x1085 runnable [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007fe48c0ba000 nid=0x1084 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007fe48c0b7000 nid=0x1083 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007fe48c0b5000 nid=0x1082 runnable [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007fe48c082000 nid=0x1081 in Object.wait() [0x00007fe477cfb000]
       java.lang.Thread.State: WAITING (on object monitor)
    	at java.lang.Object.wait(Native Method)
    	- waiting on <0x00000000ec588ee8> (a java.lang.ref.ReferenceQueue$Lock)
    	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
    	- locked <0x00000000ec588ee8> (a java.lang.ref.ReferenceQueue$Lock)
    	at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
    	at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
    
    "Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007fe48c07d800 nid=0x1080 in Object.wait() [0x00007fe477dfc000]
       java.lang.Thread.State: WAITING (on object monitor)
    	at java.lang.Object.wait(Native Method)
    	- waiting on <0x00000000ec586c00> (a java.lang.ref.Reference$Lock)
    	at java.lang.Object.wait(Object.java:502)
    	at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
    	- locked <0x00000000ec586c00> (a java.lang.ref.Reference$Lock)
    	at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
    
    "VM Thread" os_prio=0 tid=0x00007fe48c074000 nid=0x107f runnable 
    
    "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007fe48c01e800 nid=0x107d runnable 
    
    "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007fe48c020800 nid=0x107e runnable 
    
    "VM Periodic Task Thread" os_prio=0 tid=0x00007fe48c0e1800 nid=0x1086 waiting on condition 
    
    JNI global references: 310
    
  • jconsole 来查看某个 Java 进程中线程的运行情况(图形界面)

jconsole 远程监控配置

  • 需要以如下方式运行你的 java 类

    java -Djava.rmi.server.hostname=`ip地址` -Dcom.sun.management.jmxremote -
    Dcom.sun.management.jmxremote.port=`连接端口` -Dcom.sun.management.jmxremote.ssl=是否安全连接 -
    Dcom.sun.management.jmxremote.authenticate=是否认证 java类
    
  • 关闭防火墙,允许端口

  • 修改 /etc/hosts 文件将 127.0.0.1 映射至主机名

如果要认证访问,还需要做如下步骤

  • 复制 jmxremote.password 文件
  • 修改 jmxremote.password 和 jmxremote.access 文件的权限为 600 即文件所有者可读写
  • 连接时填入 controlRole(用户名),R&D(密码)

∗ 4. 原理之线程运行 \textcolor{Green}{*4. 原理之线程运行} 4.原理之线程运行

栈与栈帧

Java Virtual Machine Stacks (Java 虚拟机栈)

我们都知道 JVM 中由堆、栈、方法区所组成,其中栈内存是给谁用的呢?其实就是线程,每个线程启动后,虚拟 机就会为其分配一块栈内存。

  • 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
  • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

在这里插入图片描述
在这里插入图片描述

线程上下文切换(Thread Context Switch)

因为以下一些原因导致 cpu 不再执行当前的线程,转而执行另一个线程的代码

  • 线程的 cpu 时间片用完
  • 垃圾回收 (工作线程暂停)
  • 有更高优先级的线程需要运行
  • 线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法

当 Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念 就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的

  • 状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等
  • Context Switch 频繁发生会影响性能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值