并发编程的优缺点,java基础与案例开发详解知识点

并发编程的缺点

并发编程的目的就是为了能提高程序的执行效率,提高程序运行速度,但是并发编程并不总是能提高程序运行速度的,而且并发编程可能会遇到很多问题,比如:内存泄漏、上下文切换、线程安全、死锁等问题。

频繁的上下文切换

任务从保存到再加载就是一次上下文切换。

时间片是CPU分配给各个线程的时间,因为时间非常短,所以CPU不断通过切换线程,让我们觉得多个线程是同时执行的,时间片一般是几十毫秒。而每次切换时,需要保存当前的状态,以便能够进行恢复先前的状态,而这个切换时非常损耗性能,过于频繁反而无法发挥出多线程编程的优势。

减少上下文切换的解决方案

  • 无锁并发编程:可以参照concurrentHashMap锁分段的思想,不同的线程处理不同段的数据,这样在多线程竞争的条件下,可以减少上下文切换的时间。

  • CAS算法:利用Atomic下使用CAS算法来更新数据,使用了乐观锁,可以有效的减少一部分不必要的锁竞争带来的上下文切换。

  • 使用最少线程:避免创建不需要的线程,比如任务很少,但是创建了很多的线程,这样会造成大量的线程都处于等待状态。

  • 协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。

由于上下文切换也是个相对比较耗时的操作,所以在"java并发编程的艺术"一书中有过一个实验,并发累加未必会比串行累加速度要快。 可以使用Lmbench3测量上下文切换的时长 vmstat测量上下文切换次数

线程安全

多线程编程中最难以把握的就是临界区线程安全问题,稍微不注意就会出现死锁的情况,一旦产生死锁就会造成系统功能不可用。

public class DeadLockDemo {

private static String resource_a = “A”;

private static String resource_b = “B”;

public static void main(String[] args) {

deadLock();

}

public static void deadLock() {

Thread threadA = new Thread(new Runnable() {

@Override

public void run() {

synchronized (resource_a) {

System.out.println(“get resource a”);

try {

Thread.sleep(3000);

synchronized (resource_b) {

System.out.println(“get resource b”);

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

});

Thread threadB = new Thread(new Runnable() {

@Override

public void run() {

synchronized (resource_b) {

System.out.println(“get resource b”);

synchronized (resource_a) {

System.out.println(“get resource a”);

}

}

}

});

threadA.start();

threadB.start();

}

}

在上面的这个demo中,开启了两个线程threadA, threadB,其中threadA占用了resource_a, 并等待被threadB占用的resource _b。threadB占用了resource _b正在等待被threadA占用的resource _a。因此threadA,threadB出现线程安全的问题,形成死锁。同样可以通过jps,jstack证明这种推论:

“Thread-1”:

waiting to lock monitor 0x000000000b695360 (object 0x00000007d5ff53a8, a java.lang.String),

which is held by “Thread-0”

“Thread-0”:

waiting to lock monitor 0x000000000b697c10 (object 0x00000007d5ff53d8, a java.lang.String),

which is held by “Thread-1”

Java stack information for the threads listed above:

===================================================

“Thread-1”:

at learn.DeadLockDemo$2.run(DeadLockDemo.java:34)

  • waiting to lock <0x00000007d5ff53a8(a java.lang.String)

  • locked <0x00000007d5ff53d8(a java.lang.String)

at java.lang.Thread.run(Thread.java:722)

“Thread-0”:

at learn.DeadLockDemo$1.run(DeadLockDemo.java:20)

  • waiting to lock <0x00000007d5ff53d8(a java.lang.String)

  • locked <0x00000007d5ff53a8(a java.lang.String)

at java.lang.Thread.run(Thread.java:722)

Found 1 deadlock.

如上所述,完全可以看出当前死锁的情况。

那么,通常可以用如下方式避免死锁的情况:

  1. 避免一个线程同时获得多个

《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享

锁;

  1. 避免一个线程在锁内部占有多个资源,尽量保证每个锁只占用一个资源;

  2. 尝试使用定时锁,使用lock.tryLock(timeOut),当超时等待时当前线程不会阻塞;

  3. 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。

所以,如何正确的使用多线程编程技术有很大的学问,比如如何保证线程安全,如何正确理解由于JVM内存模型在原子性,有序性,可见性带来的问题,比如数据脏读,DCL等问题。而在学习多线程编程技术的过程中也会让你收获颇丰。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值