程序由指令和数据构成,但这些指令要运行,数据要读写,就必须将指令加载在CPU,数据加载进内存,在指令运行过程中还需要用到磁盘、网络等设备,进程就是用来加载指令、管理内存、管理IO的当一个程序被运行,从磁盘加载带这个程序的代码到内存,此时就开启了一个进程进程就可以认为是程序的一个实例。 线程
一个进程可以分为一到多个线程一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给CPU执行Java中线程作为最小的调度单位,进程最为资源分配的最小单位,在Windows中进程是不活动的,只是作为线程的容器 进程与线程的对比
进程基本上相互独立的,而线程存在于进程内,是进程的一个子集进程拥有共享的资源,如内存空间等,供其内部的线程共享进程间通信较为复杂
同一台计算机的进程通信称为 IPC(Inter-process communication)不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如 HTTP 线程通信相对简单,因为它们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量线程更轻量,线程上下文切换成本一般上要比进程上下文切换低 并行与并发
并发(concurrent)是同一时间应对(dealing with)多件事情的能力并行(parallel)是同一时间动手做(doing)多件事情的能力 Java线程 创建和运行线程 1. 直接使用Thread
// 创建一个线程对象
Thread t = new Thread(){
@Override
public void run(){
//要运行的任务
log.debug("running");
}
};
//设置线程的名字
t.setName("t1");
// 启动线程
t.start();
2. 使用Runable配合Thread
这个方式可以使线程和任务分开执行
Thread代表线程Runable是可执行的任务
Runnable runnable = new Runnable() {
@Override
public void run() {
log.debug("running");
}
};
// lambda 表达式写法
// Runnable runnable1 = () ->{log.debug("running");};
// 参数1是任务对象,参数2是线程的名字
Thread thread = new Thread(runnable,"t2");
thread.start();
与方法1比较
1是将线程和方法合并在了一起,2是把线程和任务分开使用Runable更容易与线程池等高级API配合用Runable让任务类脱离了Thread继承体系,更灵活 3. FutureTask配合Thread
FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况
FutureTaskstart与run 调用RunfutureTask = new FutureTask<>( new Callable () { @Override public Integer call() throws Exception { log.debug("running..."); Thread.sleep(1000); return 100; } }); Thread thread = new Thread(futureTask,"t3"); thread.start(); // 等待task返回 log.debug("{}",futureTask.get());
public static void main(String[] args) {
Thread t1 = new Thread("t1") {
@Override
public void run() {
log.debug(Thread.currentThread().getName());
}
};
t1.run();
log.debug("完成。。。。。。");
}
输出,由主线程来运行方法输出
22:03:29.423 [main] DEBUG c.Test09 - main 22:03:29.425 [main] DEBUG c.Test09 - 完成。。。。。。调用start
将上述的run改成start,输出日志如下,两个输出以不同的线程输出
22:06:42.781 [t1] DEBUG c.Test09 - t1 22:06:42.781 [main] DEBUG c.Test09 - 完成。。。。。。总结
直接调用 run 是在主线程中执行了 run,没有启动新的线程使用 start 是启动新的线程,通过新的线程间接执行 run 中的代码 sleep与yield
| 方法名 | static | 功能说明 |
|---|---|---|
| sleep(long n) | static | 让当前执行的线程休眠n毫秒,休眠时让出 cpu的时间片给其它线程 |
| yield() | static | 提示线程调度器让出当前线程对CPU的使用 |
- 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException睡眠结束后的线程未必会立刻得到执行建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性
- 调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程具体的实现依赖于操作系统的任务调度器
线程对象可以通过setPriority();方法来设定优先级线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用 join方法
join()方法分为有参和无参,
| 方法名 | static | 功能说明 |
|---|---|---|
| join | 等待线程运行结束 | |
| join(long n) | 等待线程运行结束,最多等待 n毫秒 |
static int r1 = 0;
static int r2 = 0;
public static void main(String[] args) throws InterruptedException {
test12();
}
private static void test12() throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
r1 = 10;
});
Thread t2 = new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
r2 = 20;
});
long start = System.currentTimeMillis();
t1.start();
t2.start();
t1.join();
t2.join();
long end = System.currentTimeMillis();
log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}
第一个 join:等待 t1 时, t2 并没有停止, 而在运行第二个 join:1s 后, 执行到此, t2 也运行了 1s, 因此也只需再等待 1s输出为
22:31:55.281 [main] DEBUG c.Test12 - r1: 10 r2: 20 cost: 2005join(long n) 等待够时间
static int r1 = 0;
static int r2 = 0;
public static void main(String[] args) throws InterruptedException {
test11();
}
public static void test11() throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
r1 = 10;
});
long start = System.currentTimeMillis();
t1.start();
// 线程执行结束会导致 join 结束
t1.join(1500);
long end = System.currentTimeMillis();
log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}
输出
22:34:22.580 [main] DEBUG c.Test11 - r1: 10 r2: 0 cost: 1006等待超时
static int r1 = 0;
static int r2 = 0;
public static void main(String[] args) throws InterruptedException {
test13();
}
public static void test13() throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
r1 = 10;
});
long start = System.currentTimeMillis();
t1.start();
// 线程执行结束会导致 join 结束
t1.join(1500);
long end = System.currentTimeMillis();
log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}
输出
22:36:17.886 [main] DEBUG c.Test13 - r1: 0 r2: 0 cost: 1506interrupt 方法
打断 sleep,wait,join 的线程,这几个方法都会让线程进入阻塞状态打断 sleep 的线程, 会清空打断状态,以 sleep 为例
private static void test1() throws InterruptedException {
Thread t1 = new Thread(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t1");
t1.start();
Thread.sleep(500);
t1.interrupt();
log.debug(" 打断状态: {}", t1.isInterrupted());
}
输出
java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.wjl.code01.Test01.lambda$test1$0(Test01.java:18) at java.lang.Thread.run(Thread.java:750) 22:39:48.973 [main] DEBUG c.Test01 - 打断状态: false
打断正常运行的线程,不会清空打断状态
private static void test2() throws InterruptedException {
Thread t2 = new Thread(()->{
while(true) {
Thread current = Thread.currentThread();
boolean interrupted = current.isInterrupted();
if(interrupted) {
log.debug(" 打断状态: {}", interrupted);
break;
}
}
}, "t2");
t2.start();
Thread.sleep(500);
t2.interrupt();
}
输出
22:42:04.128 [t2] DEBUG c.Test01 - 打断状态: true



