Java并发的学习与理解

进程与线程

进程与线程

在这里插入图片描述
在这里插入图片描述

并发与并行

并发:同一时间段处理(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,并不开启新线程,还是只有一个主线程mainstart方法不能调用两次!!!会爆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加在方法上

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值