并发编程把我整的是服服气气的了

简介: 阿粉因为原来的编程习惯,已经很久没有去考虑并发的问题了,结果之前在面试的问题就回答的不是很完善,而阿粉也用心学习了并发编程这一块的所有内容,一起来分享给大家。

为什么需要并发编程

因为现在的CPU我们大家也都知道,什么几核几线程,各种各样,而我们并发编程的目的是为了让程序运行得更快,这里的更快说的并不是让我们无限制启动更多的线程就能让程序进行最大可能的并发操作,但是我们在进行并发编程的时候,很容易遇到很多的问题,比如说死锁问题,再比如说上下文的切换的问题,这都是问题所在。

实现多线程的几种方式,面试中最简单的题目

说起来这个面试题,很多回答都一样,

  • 继承Thread类
  • 实现Runnable接口
  • 使用线程池

这是很多面试者回答的时候总是回答这三个,但是实际上,实现多线程的方式也不限于这几种方式,还有比如说带返回值的线程实现,定时器实现,内部类实现,这些方式都是可以实现多线程的。那我们今天就先来把这些不常用的方式来梳理一下。

使用匿名内部类的方式实现多线程

其实说实话,这匿名内部类的方式也不能算是一种新的实现方式,只不过是把这个实现方式放到了匿名类里面了,实现的总体内部还是使用的继承 Thread和实现Runnable接口。

案例实现:

public class TestClass {
    public static void main(String[] args) {
        // 基于子类的方式
        new Thread() {
            @Override
            public void run() {
                while (true) {
                    printThreadInfo();
                }
            }
        }.start();
        // 基于接口的实现
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    printThreadInfo();
                }
            }
        }).start();
    }
    private static void printThreadInfo() {
        System.out.println("当前运行的线程名为: " + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
实现结果:
当前运行的线程名为:Thread-1
当前运行的线程名为:Thread-0
当前运行的线程名为:Thread-1
当前运行的线程名为:Thread-0
当前运行的线程名为:Thread-1
当前运行的线程名为:Thread-0
当前运行的线程名为:Thread-0
当前运行的线程名为:Thread-1
当前运行的线程名为:Thread-1
当前运行的线程名为:Thread-0
当前运行的线程名为:Thread-0
当前运行的线程名为:Thread-1

其实对于上述手段,大家也肯定都会,那么我们就说说这个定时器实现方式,这个方式实际上是也是大家经常会使用的一种方式,因为我们很多时候都需要在我们不在的情况下进行一些操作,比如说,每天晚上对系统进行一下当天的统计操作什么的。

使用定时器实现

public class TestClass {
    private static final SimpleDateFormat dateFormat =
            new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    public static void main(String[] args) throws Exception {
        // 创建定时器
        Timer timer = new Timer();
        // 提交计划任务
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时任务执行了...");
            }
        }, dateFormat.parse("2020-12-08 20:30:00"));
    }
}
这段代码大家可以复制一下,在你设定好的时间内进行执行

关于多线程的实现方式,阿粉就给大家讲述到这里,毕竟这个东西在你使用的时候,一定是活学活用的,不是一成不变的,需要你看自己的需求来弄。

接下来我们就先从并发编程的线程安全性开始入手,接下来阿粉也会继续给大家更新关于并发编程的各种技术内容,让大家能够尽快的掌握好这个线程安全的问题,

线程的安全性操作

其实对于一个对象来说,他是否是线程安全的,完全取决于他是否被多个线程去访问,而如果要让我们的对象是线程安全的话,那么我们一定要采取一些方式,而方式都有哪些呢?

  • 同步机制
  • 加锁机制

也就是大家所了解的同步 Synchronized 和加锁的机制。还有就是使用Volatile类型的变量。

也就是说,如果多个线程去访问同一个可变的状态的变量的时候,没有使用合适的同步,那么程序相对来说就会出现错误,而解决方式也有好几种,

  • 比如说不在线程之前共享这个变量
  • 将状态变量修改成为不可变的的变量
  • 在访问状态变量的时候使用同步

而阿粉之前也看过一个图片,就是说他从字节码的角度去分析了线程不安全的操作,看下图

42.jpg

用一个最简单的案例给大家讲解Synchronized,我们手动实现一个线程然后递减,每次输出这个变量,最终看效果图

public class TestClass implements Runnable{
    int i = 100;
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while(true) {
            if(i>0) {
                try {
                    Thread.sleep(10);//为了让安全问题明显,我们让线程执行的时间变长,故睡眠10毫秒
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println(i);
                i--;
            }
        }
    }
}
class Test{
    public static void main(String[] args) {
        TestClass testClass = new TestClass();
        Thread t1 = new Thread(testClass);
        Thread t2 = new Thread(testClass);
        Thread t3 = new Thread(testClass);
        t1.start();
        t2.start();
        t3.start();
    }
}

不用说大家都知道,结果肯定是乱的一塌糊涂,有来回跳跃的,也有分段执行的,反正就是不是从100到1的,结果大家可以把代码拿过去使用一下自己看看。

那么我们加上Synchronized关键字之后呢?

public class TestClass implements Runnable{
    int i = 100;
    @Override
    public void run() {
        while(true) {
            synchronized (this){
                if(i>0) {
                    try {
                        Thread.sleep(10);//为了让安全问题明显,我们让线程执行的时间变长,故睡眠10毫秒
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(i);
                    i--;
                }
            }
        }
    }
}
class Test{
    public static void main(String[] args) {
        TestClass testClass = new TestClass();
        Thread t1 = new Thread(testClass);
        Thread t2 = new Thread(testClass);
        Thread t3 = new Thread(testClass);
        t1.start();
        t2.start();
        t3.start();
    }
}

大家可以去执行一下运行结果,顺带打印出执行结果,是不是这次就很舒服了,终于看到自己心心念念的从100-1的内容了,而实际上,我们只是通过加上了一个同步的关键字,来实现了线程的安全性操作,让线程同步执行,不再会出现那个不安全的行为,是不是很简单?你学会了么?

下一篇文章阿粉将会带给大家关于另外的一个关键字 Volatile 实现线程安全,并且告诉大家他的可见性,还有原子性。

文献参考 《Java并发编程艺术》 《Java并发编程》

相关文章
|
存储 机器学习/深度学习 搜索推荐
探索医学影像:如何通过ROI灰度直方图和ROI区域方格图揭示隐秘细节?
探索医学影像:如何通过ROI灰度直方图和ROI区域方格图揭示隐秘细节?
494 1
|
IDE 物联网 网络性能优化
什么是MQTT?如何使用ESP12F芯片连接到MQTT服务器
通过上述步骤,你可以成功地使用ESP12F模块连接到MQTT服务器,发布和订阅消息。MQTT的轻量级和高效性使其非常适合各种物联网应用,而ESP12F模块的强大功能和低成本使其成为实现这些应用的理想选择。
501 0
Elementplus淡入淡出效果,头部顶栏如何设置文字隐藏效果,默认图标如何收缩,icons如何通过类进行替换,侧边栏如何添加阴影,右边如何设置高度,侧边栏如何设置阴影,如何让icon与文字
Elementplus淡入淡出效果,头部顶栏如何设置文字隐藏效果,默认图标如何收缩,icons如何通过类进行替换,侧边栏如何添加阴影,右边如何设置高度,侧边栏如何设置阴影,如何让icon与文字
|
SpringCloudAlibaba Java 数据库连接
引入SpringCloud Alibaba(多配置集、GateWay)
引入SpringCloud Alibaba(多配置集、GateWay)
373 0
|
存储 监控 网络协议
什么是RTSP
RTSP,即实时流传输协议,英文全称为Real-Time Streaming Protocol
951 0
|
机器学习/深度学习 算法 搜索推荐
多任务学习模型之DBMTL介绍与实现
本文介绍的是阿里在2019年发表的多任务学习算法。该模型显示地建模目标间的贝叶斯网络因果关系,整合建模了特征和多个目标之间的复杂因果关系网络,省去了一般MTL模型中较强的独立假设。由于不对目标分布做任何特定假设,使得它能够比较自然地推广到任意形式的目标上。
|
存储 容灾 Linux
UOS统一操作系统,让我们拥抱中文操作系统,打造属于自己的私人企业级网盘
UOS统一操作系统,让我们拥抱中文操作系统,打造属于自己的私人企业级网盘
719 0
UOS统一操作系统,让我们拥抱中文操作系统,打造属于自己的私人企业级网盘
|
缓存 Java 数据库连接
提高检索效率的利器--Mybatis 的一级缓存和二级缓存执行顺序
提高检索效率的利器--Mybatis 的一级缓存和二级缓存执行顺序
356 0
|
Windows
Windows 右键 图标-菜单无法打开-资源管理器重启-explorer.exe停止与 Windows 交互并关闭【已解决】
Windows 右键 图标-菜单无法打开-资源管理器重启-explorer.exe停止与 Windows 交互并关闭【已解决】
789 0
Windows 右键 图标-菜单无法打开-资源管理器重启-explorer.exe停止与 Windows 交互并关闭【已解决】
阿里云账号个人实名认证图文教程(新手攻略)
阿里云个人账号实名认证可以使用支付宝认证,快速通过
2725 0
阿里云账号个人实名认证图文教程(新手攻略)