Java并发编程
进程与线程
进程
-
程序由指令和数据构成,但这些指令要运行,数据要读写,就必须将指令加载在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 类型的参数,用来处理有返回结果的情况
FutureTask<Integer> futureTask = new FutureTask<>(
new Callable<Integer>() {
@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());
start与run
调用Run
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
-
调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞) -
其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException -
睡眠结束后的线程未必会立刻得到执行 -
建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性
yield
-
调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程 -
具体的实现依赖于操作系统的任务调度器
线程优先级的设定
-
线程对象可以通过 setPriority();
方法来设定优先级 -
线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它 -
如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用
join方法
join()方法分为有参和无参,
方法名 | static | 功能说明 |
---|---|---|
join | 等待线程运行结束 | |
join(long n) | 等待线程运行结束,最多等待 n毫秒 |
join()
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: 2005
join(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: 1506
interrupt 方法
-
打断 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
原文始发于微信公众号(卫颓废):Java并发编程(一)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/47492.html