每日10题 2020-12-28

进程与线程

进程就是一个在内存中运行的应用程序。进程就是用来加载指令、管理内存、管理 IO 的,是资源分配的基本单位
线程是系统能够进行运算调度的最小单位,一个进程包含多个线程

二者对比

进程基本上相互独立的,而线程存在于进程内,是进程的一个子集
进程拥有共享的资源,如内存空间等,供其内部的线程共享
进程间通信较为复杂
同一台计算机的进程通信称为 IPC(Inter-process communication)
不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如 HTTP
线程通信相对简单,因为它们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量
线程更轻量,线程上下文切换成本一般上要比进程上下文切换低

并行与并发

并发(concurrent)是同一时间应对(dealing with)多件事情的能力
并行(parallel)是同一时间动手做(doing)多件事情的能力

同步和异步调用

需要等待结果返回,才能继续运行就是同步
不需要等待结果返回,就能继续运行就是异步

创建和运行线程

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();

2.使用 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();

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

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

方法1 是把线程和任务合并在了一起,方法2 是把线程和任务分开了

import jdk.swing.interop.SwingInterOpUtils;

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

class ThreadTest01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Thread t1 = new Thread("t1") {
            public void run() {
                System.out.println("t1线程执行");
            }
        };
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("t2线程执行");
                try {
                    t1.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("t2线程结束");
            }
        };
        Runnable runnable1 = () -> System.out.println("t3线程执行");
        FutureTask<Integer> futureTask = new FutureTask<Integer>(() -> {
            System.out.println("t4线程执行");
            return 100;

        });
        Thread t2 = new Thread(runnable);
        Thread t3 = new Thread(runnable1,"t3");
        Thread t4 = new Thread(futureTask,"t4");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t4.sleep(10000);//阻塞的是主线程

        Integer result = futureTask.get();//不是t4.get()
        try {
            t1.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main线程执行");
        System.out.println(result);
    }
}

t1线程执行
t2线程执行
t3线程执行
t4线程执行
t2线程结束
main线程执行
100

Process finished with exit code 0

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

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

  • 垃圾回收
  • 有更高优先级的线程需要运行
  • 线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法

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

start 与 run

public static void main(String[] args) {
	Thread t1 = new Thread("t1") {
		@Override
		public void run() {
			log.debug(Thread.currentThread().getName());
			FileReader.read(Constants.MP4_FULL_PATH);
		}
	};
	t1.run();//程序仍在 main 线程运行, FileReader.read() 方法调用还是同步的
	t1.start(); //程序在 t1 线程运行, FileReader.read() 方法调用是异步的
	log.debug("do other things ...");
}

sleep 与 yield

sleep
1. 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
2. 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
3. 睡眠结束后的线程未必会立刻得到执行
4. 建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性

yield
5. 调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程
6. 具体的实现依赖于操作系统的任务调度器

        Runnable run1 = () -> {
            int count = 0;
            for(; ;){
                System.out.println("t1线程--->" + count++);
            }
        };
        Runnable run2 = () -> {
            int count = 0;
            for(; ;){
                System.out.println("t2线程--->" + count++);
            }
        };
        Thread t1 = new Thread(run1,"t1");
        Thread t2 = new Thread(run2,"t2");
        t1.setPriority(10);
        t2.setPriority(1);
        t1.start();
        t2.start();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值