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

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

为什么需要并发编程

因为现在的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并发编程》

相关文章
|
8月前
|
前端开发 数据处理 Go
探秘前后端开发世界:猫头虎带你穿梭编程的繁忙街区,解锁全栈之路
探秘前后端开发世界:猫头虎带你穿梭编程的繁忙街区,解锁全栈之路
34 0
|
11月前
|
运维 数据可视化 搜索推荐
低代码和零代码二子争夺,你扶谁上位?
低代码和零代码二子争夺,你扶谁上位?
低代码和零代码二子争夺,你扶谁上位?
|
10月前
|
Java 程序员 C++
大学生如果想从事游戏行业,游戏开发或者电竞选手,有哪些建议?
@[TOC](目录) 大学生如果想从事游戏行业,游戏开发或者电竞选手,有哪些建议? # 一、游戏行业概述 游戏行业是指以电子游戏为核心的产业链,包括游戏开发、游戏发行、游戏运营、游戏硬件设备等多个环节。近年来,随着互联网技术的飞速发展,游戏行业呈现出移动化、社交化、智能化的趋势,不断涌现出新的游戏类型和玩法。游戏行业已成为全球范围内最具活力和潜力的产业之一。 # 二、游戏开发 游戏开发是指游戏制作团队根据游戏设计方案,通过编程、美术、音效等手段将游戏构思转化为具体游戏产品的过程。游戏开发涉及多个领域,包括计算机科学、数学、物理学、艺术设计等。游戏开发团队通常包括程序员、美术设计师、音效师、策划
|
机器学习/深度学习 人工智能 算法
大模型的下一步:要高精尖,更要接地气
大模型的下一步:要高精尖,更要接地气
【高并发趣事一】——Amdahl(阿姆达尔定律)与Gustafson(古斯塔夫森定律)
【高并发趣事一】——Amdahl(阿姆达尔定律)与Gustafson(古斯塔夫森定律)
292 0
【高并发趣事一】——Amdahl(阿姆达尔定律)与Gustafson(古斯塔夫森定律)
|
存储 NoSQL 算法
你管这破玩意儿叫高可用
你管这破玩意儿叫高可用
|
数据库 开发者
同城交友源码,为什么选择分布式系统架构?
同城交友源码,为什么选择分布式系统架构?
|
消息中间件 缓存 NoSQL
只要我跑的够快,内卷它就卷不到我,一名高中生是如何做到在疫情下涨薪70%的?
只要我跑的够快,内卷它就卷不到我,一名高中生是如何做到在疫情下涨薪70%的?
3178 0
|
容灾 NoSQL Redis
异地多活设计辣么难?其实是你想多了!
1. 引言 有幸参与了阿里游戏的一个高可用方案的设计,并且在网上发表了方案(面向业务的立体化高可用架构设计),后来参加GOPS全球运维大会深圳站,与众多行业高手交流,发现大家对"异地多活"这个方案设计非常感兴趣,毕竟"异地多活"的方案价值非常大
34794 2
|
数据采集 NoSQL Redis
房天下爬虫可分布式
需要观察房天下url的构造,本次爬取的是新房和二手房两个栏目的具体字段。 涉及到的知识点有url的拼接,具体字段的解析清洗,页面不规整的情况下,怎样提取。
1950 0