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

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

为什么需要并发编程

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

相关文章
|
7月前
|
数据采集 API 开发者
Roxlabs:解锁高效数据采集与网络应用新境界
Roxlabs以其先进的技术和优质服务,在全球IP代理服务领域中确立了领航者的地位。无论客户需求如何多变,Roxlabs都能提供符合高标准的解决方案,助力企业和个人用户在数字化时代里畅游无界。
87 0
Roxlabs:解锁高效数据采集与网络应用新境界
|
4月前
|
Java UED 开发者
揭开Java性能提升之谜:异步编程如何让你的应用响应速度飞升?
【8月更文挑战第30天】随着互联网技术的发展,系统性能和用户体验成为关注焦点,异步编程因其能提高应用响应速度和吞吐量而在Java领域广泛应用。本文将详细阐述Java异步编程的概念与优势,并通过实战示例展示其在实际项目中的应用,如使用`Future`、`Callable`及`CompletableFuture`等接口和类实现异步操作,帮助开发者更好地理解和运用这一技术,以提升程序性能和用户体验。
33 0
|
4月前
|
安全 Java 调度
震撼揭秘!手撕并发编程迷雾,Semaphore与CountDownLatch携手AQS共享模式,让你秒懂并发神器背后的惊天秘密!
【8月更文挑战第4天】在Java并发编程中,AbstractQueuedSynchronizer (AQS) 是核心框架,支持独占锁与共享锁的实现。本文以Semaphore与CountDownLatch为例,深入解析AQS共享模式的工作原理。Semaphore通过AQS管理许可数量,控制资源的并发访问;而CountDownLatch则利用共享计数器实现线程间同步。两者均依赖AQS提供的tryAcquireShared和tryReleaseShared方法进行状态管理和线程调度,展示了AQS的强大功能和灵活性。
42 0
|
6月前
|
Java 调度
【实战指南】Java多线程高手秘籍:线程生命周期管理,掌控程序命运的钥匙!
【6月更文挑战第19天】Java多线程涉及线程生命周期的五个阶段:新建、就绪、运行、阻塞和死亡。理解这些状态转换对性能优化至关重要。线程从新建到调用`start()`变为就绪,等待CPU执行。获得执行权后进入运行状态,执行`run()`。遇到阻塞如等待锁时,进入阻塞状态。完成后或被中断则死亡。管理线程包括合理使用锁、利用线程池、处理异常和优雅关闭线程。通过控制这些,能编写更高效稳定的多线程程序。
46 1
|
7月前
|
消息中间件 缓存 监控
直呼内行!阿里大佬离职带出内网专属“高并发系统设计”学习笔记
我们知道,高并发代表着大流量,高并发系统设计的魅力就在于我们能够凭借自己的聪明才智设计巧妙的方案,从而抵抗巨大流量的冲击,带给用户更好的使用体验。这些方案好似能操纵流量,让流量更加平稳得被系统中的服务和组件处理。
|
SQL 人工智能 文字识别
居家办公更要高效-自动化办公完美提升摸鱼时间
不管是线下办公,还是居家办公,摸鱼必不可少(当然不提倡摸鱼),可是摸鱼归摸鱼,工作总得完成,KPI得好看,才能走向人生巅峰。不然月月3.25,年年得吃土,你有多痛苦,老板不清楚,让你加入毕业队伍,你只会大骂人心不古。押韵吧,KPI要好看,活得干的好,干的有效率,平时还想摸摸鱼,那摸鱼神器不得备一套,额。。。不,是高效办公神器必须得攒一套。这不,自动化办公的神器双手奉上,废话不多说,上才艺。 说到办公,每天都少不了要和各种文档打交道,csv,excel,word,ppt,pdf甚至txt文本文件,需要对这些文档做各种操作,有很多还是比较机械化的重复工作,枯燥且无味,花时间勉强能够处理,就是有点废
311 1
|
前端开发 数据处理 Go
探秘前后端开发世界:猫头虎带你穿梭编程的繁忙街区,解锁全栈之路
探秘前后端开发世界:猫头虎带你穿梭编程的繁忙街区,解锁全栈之路
52 0
|
运维 数据可视化 搜索推荐
低代码和零代码二子争夺,你扶谁上位?
低代码和零代码二子争夺,你扶谁上位?
低代码和零代码二子争夺,你扶谁上位?
满地坑!细数IO操作的几个坑
IO是我们日常开发中经常使用到的技能,但是一不小心我们就会踩坑,今天梳理了我在工作中遇到的一些问题
|
存储 安全 算法
重生之我在人间敲代码_Java并发基础_安全性、活跃性以及性能问题
并发编程中我们需要注意的问题有很多,很庆幸前人已经帮我们总结过了,主要有三个方面,分别是:安全性问题、活跃性问题和性能问题。