进程与线程
进程与线程
并发与并行
并发:同一时间段处理(deal with)多个任务的能力
并行:同一时间段做(doing)多个任务的能力
多线程的应用(异步)
Java开启多线程后,执行线程的start操作后,线程执行的顺序不分先后,而且线程可以开启多个,同一时间可以多个线程同时工作。
多核情况下,多线程中的每个线程都有一个核去工作,单核情况下则即使开了多线程,这些线程也是轮流执行的。
创建和运行线程
用 new Thread()
//参数是该线程名称
Thread thread = new Thread("线程名"){
@Override
public void run() {
//线程执行任务在这里写
}
};
//启动线程
thread.start();
用Runnable接口
Runnable runnable = new Runnable() {
@Override
public void run() {
//在这里写该线程要执行的代码
}
};
//第一个参数是功能对象,第二个参数是线程名
Thread thread = new Thread(runnable, "线程名");
thread.start();
lambda简化runnable接口版本
@FunctionalInterface注解表示当前接口可以使用lambda表达式,使用该注解的注意事项如下
1.接口有且仅有一个抽象方法
2.允许定义静态方法
3.允许定义默认方法
4.允许java.lang.Object中的public方法
该注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错
//第一种lambda写法
Runnable runnable=()->{
//该线程要执行的代码
};
Thread thread = new Thread(runnable, "线程名");
thread.start();
//第二种lambda写法
new Thread(()->{
//该线程要执行的代码
},"线程名").start();
FutureTask配合Thread
//此方法可以拿到线程执行后的返回值
FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
//该线程执行的逻辑与返回值
System.out.println("执行。。。");
return 66;
}
});
//线程执行
Thread thread = new Thread(task, "线程名");
thread.start();
//拿到该线程的执行后的返回值,一定要放到start方法后,否则拿返回值在执行逻辑前,是矛盾的
System.out.println(task.get());
Thread与Runnable关系
当使用new Thread(runnable)调用线程时,实际上就是使用runnable重写的run方法去执行代码逻辑。若使用new Thread()没有传runnable调用线程时,则是Thread自己的run方法执行代码逻辑。
线程的查看与杀死
Windows环境下查找Java运行进程并强制杀死
多线程运行状态原理(查看方法)
假如程序有多个线程,可以在断点出右键选择如图下所示,就可以查看某个线程具体的运行情况。
可以看到两个线程的栈帧都不一样,活动栈帧也不一样。多线程之间的栈与栈帧互不干扰。如下图所示。
线程上下文切换
常见方法
start与run区别
start是开启一个新的线程来运行这个新线程的方法,run并没有开启一个新的线程而是用主线程来自己线程内的方法。如下面两个图所示。使用start方法,t1线程和主线程交替运行。使用run方法,t1线程内的方法执行完了,主线程的方法开始运行。使用run,并不开启新线程,还是只有一个主线程main,start方法不能调用两次!!!会爆IllegalThreadStateException错误
sleep与yield
下图的Timed Waiting与Runnable的区别在于:后者可以随时被CPU时间片调度并执行,前者不可以
sleep的应用
下图是单核机器运行
如下图所示:单核情况下,如果程序一直while(true)死循环,则CPU使用率很容易百分之百,加上sleep以后,使用率下降很多。
join与join的应用
join:等待线程调用结束,谁调用join,就等待谁结束
下面两个图的代码是主线程与t1线程两个线程,当t1线程调用join方法后,表示主线程等待t1线程完成后,主线程再执行。
使用join的注意事项
如下图所示:join方法针对的是当前线程调用子线程,第一张图中,主线程调用了t1与t0线程,阻塞的只是主线程,t1与t0线程是并行,第二张图中,主线程调用t1线程的Join,t1线程调用t0线程的join,这才会让t1与t0是串行关系,t0结束后t1再执行。
也就是说,第一张图运行时间应该是2秒左右,第二张图运行时间应该是3秒左右
有时间限制的join等待,这里需要注意的是如果join等待时间内,该线程完成工作,则自动释放join方法
interrupt与interrupt的应用
interrupt是打断的意思,可以利用interrupt打断线程,然后根据打断标记为true来让某个线程停止工作,假如某个线程在执行sleep,wait,join这三个方法中执行了interrupt,则会抛出一个打断的异常,这时这个线程的打断标记为false
两阶段中止模式
public class 二阶段停止 {
public static void main(String[] args) throws InterruptedException {
TwoPhaseTermination twoPhaseTermination = new TwoPhaseTermination();
twoPhaseTermination.start();
Thread.sleep(4500);
twoPhaseTermination.stop();
}
}
class TwoPhaseTermination{
private Thread tpt;
public void start(){
tpt=new Thread("t1"){
@Override
public void run() {
while (true){
//interrupted()也可使用这个,这个与isInterrupted区别是:
//interrupted()是静态,会清除标记
//isInterrupted()非静态,不会清除标记
if(currentThread().isInterrupted()){
System.out.println("tpt被二阶段中止");
break;
}else{
System.out.println("正在监控。。。");
try {
//模拟业务
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
//这里的异常会将打断标识设置为false,所以需要再次打断
currentThread().interrupt();
}
}
}
}
};
tpt.start();
}
public void stop(){
tpt.interrupt();
}
}
interrupt-打断park
Java里有个方法LockSupport.park(),会把当前线程定在这,可以使用interrupt让该线程继续执行。如果线程的interrupt标记为false,则park不生效。
Thread thread = new Thread("t1") {
@Override
public void run() {
log.info("t1线程开始运行");
LockSupport.park();
log.info("t1线程的park被打断");
//使用interrupted()判断线程是否被interrupt后,再次调用park方法还会定在这
//因为interrupted()这个静态方法把interrupt标记改成了false
log.info("t1线程的interrupt状态:"+interrupted());
LockSupport.park();
//使用currentThread().isInterrupted()判断线程是否被interrupt后,再次调用park方法不会定在这
//因为currentThread().isInterrupted()这个方法不会把interrupt标记改成了false
log.info("t1线程的interrpt状态:"+currentThread().isInterrupted());
}
};
thread.start();
Thread.sleep(1000);
thread.interrupt();
守护线程
五种状态
从代码上来讲:
1.初始状态:new一个Thread类,未start
2.可运行状态:new的Thread类,已经start,但是CPU没来得及处理该线程
3.运行状态,new的Thread类,已经start,并且CPU来处理该线程了
4.阻塞状态,new的Thread类,已经start,并且CPU也来处理了,但是该线程正在做一些IO流的操作,需要执行完才能做其他
5.终止状态:new的Thread类,已经start,CPU已经将该线程执行完了,该线程终止运行。
六种状态
从代码上来讲:
1.NEW状态:new一个Thread类,未start
2.RUNABLE状态:new的Thread类,已经start,但是CPU没来得及运行该线程,或者CPU正在运行该线程,或者CPU正在运行该线程且该线程正在执行一些流操作“阻塞”这里时。
3.BLOCKED状态:new的Thread类,已经start,抢锁的时候发现锁被抢,需要等待锁被释放才能继续执行,就会等待锁进入BLOCKED状态。
4.WAITING状态:new的Thread类,已经start,里面加入了join方法,需要等join方法的执行类运行完才能执行自己的代码,什么时候执行完不确定,所以是没有TIMED的WAITING状态。
5.TIMED_WAITING状态:new的Thread类,已经start,执行了Thread.sleep方法,有具体的睡眠时间,所以是加了TIMED的WAITING状态。
6.TERMINATED状态:new的Thread类,已经start,且执行完毕结束。
public static void main(String[] args) {
Thread t1 = new Thread("t1") {
@Override
public void run() {
}
};
Thread t2 = new Thread("t2") {
@Override
public void run() {
synchronized (com.多线程.简单多线程.State.class) {
while (true) {
}
}
}
};
t2.start();
Thread t3 = new Thread("t3") {
@SneakyThrows
@Override
public void run() {
Thread.sleep(200000l);
}
};
t3.start();
Thread t4 = new Thread("t4") {
@SneakyThrows
@Override
public void run() {
t2.join();
}
};
t4.start();
Thread t5 = new Thread("t5") {
@Override
public void run() {
synchronized (com.多线程.简单多线程.State.class){
}
}
};
t5.start();
Thread t6 = new Thread("t6") {
@Override
public void run() {
}
};
t6.start();
log.info("t1:"+t1.getState()); //NEW
log.info("t2:"+t2.getState()); //RUNNABLE
log.info("t3:"+t3.getState()); //TIMED_WAITING
log.info("t4:"+t4.getState()); //WAITING
log.info("t5:"+t5.getState()); //BLOCKED
log.info("t6:"+t6.getState()); //TERMINATED
}
统筹规划
本章小结
共享模型之管程
并发带来的问题
上下文切换
下面的代码,运行以后,结果并不是0,这是因为线程t1与t2在对count加1和减1的时候,并不是一步完成的。
count++在字节码中会产生4条JVM指令,如下图所示
所以当i++走完iadd时,i为1,可能这时该线程时间片用完了,轮到i–了,i–可能将i变成-1赋值给i,此时时间片再给自增的线程,i又会变成1,则i–的操作就失效了。
临界区与竞态条件
synchronized解决方案
下面代码执行结果就是0
t1与t2之间的synchronized时序图
synchronized的思考
1.如果放到for循环外,依旧是正确结果,当锁在外面时,意味着t1或者t2线程执行完5000次循环后再执行另外那个线程。
2.如果t1加obj1锁,t2加obj2锁,则意味着t1和t2线程两者运行互相不干扰,相当于没有加锁。
3.如果t1加了obj锁,t2没有加,和第2种情况一样呢。
对象当作锁
public class 对象当锁 {
public static void main(String[] args) throws InterruptedException {
Room room = new Room();
Thread t1 = new Thread("t1") {
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
room.add();
}
}
};
Thread t2 = new Thread("t2") {
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
room.jian();
}
}
};
t1.start();
t2.start();
t1.join();
t2.join();
room.get();
}
}
class Room{
private Integer count=0;
public void add(){
synchronized (this) {
count++;
}
}
public void jian(){
synchronized (this) {
count--;
}
}
public void get(){
System.out.println(count);
}
}
synchronized加在方法上