自学内容网 自学内容网

线程和进程(juc)

线程

一:概念辨析

1:线程与进程

进程:

1:程序由指令和数据组成,指令要执行,数据要读写,就需要将指令加载给cpu,把数据加载到内存,同时程序运行时还会使用磁盘,网络等资源。进程就是负责管理内存,加载指令,管理io的;

2:当一个程序运行时就会将程序的相关代码加载到内存中,这就开启了一个进程;

3:一个程序的实例就是进程,有的程序可以有多个实例进程,有的程序只能有一个实例进程;


线程:

1:一个进程内会有一个或多个线程

2:一个线程就是一个指令流,将指令一条条按照顺序加载给cpu执行;

3:在java中,线程是最小调度单位,进程是最小资源管理单位。在windows中进程是不活动的只是作为线程的容器;


线程和进程对比:

1:进程是相对独立的,线程存在于进程,是进程的一个子集;

2:进程有共享的资源,如共享的内存;

3:进程间的通信,分为同一个计算机和不同计算机:

同一个计算机进行通信较为简单,称为ipc;

不同计算机的进程进行通信需要借助于网络,并且遵守相同的协议如http;

4:线程间通信较为简单,因为他们共享进程的资源;

5:线程更轻量,线程切换上下文比进程切换上下文的成本更低;

2:并发和并行的概念:

并发:在单核cpu下线程实际上是串行执行的,操作系统中有个组件叫任务调度器,它会将cpu的时间片轮流交给不同的线程使用,由于时间片很短,cpu在不同的线程进行切换人类感知不到,所以就会感觉是同时运行的。总结一下就是:微观串行,宏观并行

像这种,线程轮流使用cpu的情况就叫做并发;

而在多核cpu下,每个核都可以调度线程,这个时候就是并行的;

使用golang的创作者的一句话就是:并发:是同时应对多件事的能力;(deal with)

并行:是同时做多件事的能力;(do)

二:应用

1:异步调用:

同步调用:需要等待结果返回才能执行下面的代码;

异步调用:无需等待结果的返回就能执行下面的代码;

2:提升效率:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意单核cpu使用多线程与单核cpu使用单线程的性能差距不是很大;

多核cpu和单核cpu执行的性能有很大差别;

三:创建

1:使用thread的内部类

public static void main(String[] args) {
    Thread t=new Thread(){
        @Override
        public void run() {
            log.debug("new thread");
        }
    };
    t.setName("t1");
    t.start();
    log.debug("main");
}

t.setName(“t1”);来给线程命名。run方法指定要执行的任务,start启动线程;

2:使用runnable配合thread

将线程和任务分开:runnable来定义任务,thread创建线程:

public static void main(String[] args) {
   Runnable runnable= new Runnable(){
        @Override
        public void run() {
            log.debug("running");
        }
    };
    Thread thread = new Thread(runnable);
    thread.setName("t1");
    thread.start();
    log.debug("running");
}

java8之后可以使用lamda表达式来简化语句:

public static void main(String[] args) {
  /* Runnable runnable= new Runnable(){
        @Override
        public void run() {
            log.debug("running");
        }
    };*/
    Runnable runnable =() ->log.debug("running");
    Thread thread = new Thread(runnable);
    thread.setName("t1");
    thread.start();
    log.debug("running");
}

或者:

public static void main(String[] args) {
  /* Runnable runnable= new Runnable(){
        @Override
        public void run() {
            log.debug("running");
        }
    };*/
 /*   Runnable runnable =() ->log.debug("running");*/
    Thread thread = new Thread(()->log.debug("running"),"t1");
    /*thread.setName("t1");*/
    thread.start();
    log.debug("running");
}

3:使用futureTask配合thread

public static void main(String[] args) throws ExecutionException, InterruptedException {
    FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
            log.debug("running");
            return 100;
        }
    });
    Thread thread=new Thread(integerFutureTask,"t1");
    thread.start();
    log.debug("{}",integerFutureTask.get());

}

futuretask继承了runnable接口,futuretask能够接收callable参数;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

调用get方法时会阻塞等待,等待线程执行完毕然后返回结果;

四:查看

1:windows

window查看进程:

  • 使用任务管理器查看
  • 命令行:tasklist:查看所有进程
  • 杀死进程:taskkill

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2:linux

linux查看进程:

  • ps命令,结合管道运算符。
  • top命令动态的查看进程;

杀死进程:

  • kill 【进程id】

查看线程:

  • top -H -p 【进程id】

-H表示查看线程,-p指定进程id;

3:java

  • 查看进程:jps
  • 查看线程:jstack 【进程id】

五:原理

上下文切换

  • 什么是线程的上下文切换:

    当cpu暂停执行当前线程,开始执行下一个线程的时候会产生上下文切换;

  • 什么情况下会导致上下文切换

    1:cpu的时间片用完

    2:垃圾回收

    3:优先级更高的线程执行;

    4:主动调用一些方法如sleep,yield,join,wait,lock等

    当上下文切换时就需要保存当前线程的状态,并且恢复另一个线程的状态。jvm有一个组件叫程序计数器,它的作用是来记录下一条指令的执行地址,程序计数器是线程私有的;

    保存线程的状态不止程序计数器,还需要保存虚拟机栈中的栈帧信息;

    频繁的上下文切换会影响性能;

六:常见方法

1:start和run方法

start:

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

结果:

16:20:03.992 [t1] DEBUG org.example.Field.test1 - start

run:

public static void main(String[] args) {
    new Thread(()->log.debug("run"),"t1").run();
}

结果:

16:22:07.587 [main] DEBUG org.example.Field.test1 - run

可以看到run没有创建一个新的线程,没法达到异步调用的效果;start可以;

start不能调用多次,不然会报错。

2:sleep

1:状态:
public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }, "t1");
    t1.start();
    log.debug("state:{}",t1.getState());
    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    log.debug("state:{}",t1.getState());
}

输出:

16:32:08.998 [main] DEBUG org.example.Field.test2 - state:RUNNABLE
16:32:09.512 [main] DEBUG org.example.Field.test2 - state:TIMED_WAITING

可以看到,调用sleep方法之后线程会进入time-waiting状态;

那个线程里调用sleep,那个线程就休眠

2:打断

调用interrupt方法可以打断休眠的线程,但是会抛出异常:

public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }, "t1");
    t1.start();
    log.debug("state:{}",t1.getState());
    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    t1.interrupt();
    log.debug("state:{}",t1.getState());
}

结果:

16:36:26.912 [main] DEBUG org.example.Field.test2 - state:RUNNABLE
16:36:27.416 [main] DEBUG org.example.Field.test2 - state:TIMED_WAITING
Exception in thread "t1" java.lang.RuntimeException: java.lang.InterruptedException: sleep interrupted
at org.example.Field.test2.lambda$main$0(test2.java:18)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.InterruptedException: sleep interrupted
at java.base/java.lang.Thread.sleep(Native Method)
at org.example.Field.test2.lambda$main$0(test2.java:16)
... 1 more
3:yield和sleep的区别

调用yield后线程的状态会从running状态转变为runnable就绪状态状态,然后去调用其他线程;具体的实现依赖于操作系统的任务调度器

和sleep的区别是,调用sleep进入time-wait状态不会被cpu调用,而调用yield进入就绪状态还是有可能被cpu调用的;

3:线程优先级

  • 线程的优先级会提示调度器优先执行该线程,但是只是一个提示,调度器可以忽略;
  • 当cpu较忙时会给优先级高的线程分配较长的时间片,cpu较闲时,优先级几乎没有作用;

4:join

join:等待线程运行结束;

join(时间):设置最大等待时间,如果超过最大等待时间则不再等待,如果没超过就执行完毕直接返回;

5:interrupt

打断线程

1:打断阻塞:

打断sleep,wait,join的线程;

打断后会抛出异常;

然后打断标记会变成true;

2:打断正常:

打断正常的线程不会使线程停止运行,只会将打断标记变为true;

public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(() -> {
        while (true) {

        }
    }, "t1");
    thread.start();
    Thread.sleep(1000);
    log.debug("interrupt");
    thread.interrupt();
    log.debug("is,{}",thread.isInterrupted());
}
18:43:44.479 [main] DEBUG org.example.Field.test3 - interrupt
18:43:44.481 [main] DEBUG org.example.Field.test3 - is,true

但是会一直执行;

可以进行判断然后打断线程:

public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(() -> {
        while (true) {
            Thread thread1 = Thread.currentThread();
            if (thread1.isInterrupted()) {
                log.debug("被打断了");
                break;
            }
        }
    }, "t1");
    thread.start();
    Thread.sleep(1000);
    log.debug("interrupt");
    thread.interrupt();
    log.debug("is,{}",thread.isInterrupted());
18:48:49.671 [main] DEBUG org.example.Field.test3 - interrupt
18:48:49.672 [t1] DEBUG org.example.Field.test3 - 被打断了
18:48:49.672 [main] DEBUG org.example.Field.test3 - is,true
``

原文地址:https://blog.csdn.net/2301_79748665/article/details/144299259

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!