Java并发编程之介绍

并发编程简介

将串行执行部分编程并发执行,但要考虑上下文切换和资源调度的时间

并发编程的意义

并发编程的目的是为了让程序运行得更快,但是,并不是启动更多的线程就能让程序最大限度地并发执行。

影响多线程的因素

影响多线程运行速度的原因有上下文切换死锁,以及硬件和软件的资源限制

上下文切换

即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制。时间片是CPU分配给各个线程的时间,因为时间片非常短,所以CPU通过不停地切

换线程执行,让我们感觉多个线程是同时执行的,时间片一般是几十毫秒(ms)。

CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。所以任务从保存到再加载的过程就是一次上下文切换。

上下文切换也会影响多线程的执行速度

public class ConcurrencyTest {

	/** 执行次数 */
	private static final long count = 10000l;

	public static void main(String[] args) throws InterruptedException {
		// 并发计算
		concurrency();
		// 单线程计算
		serial();
	}

	private static void concurrency() throws InterruptedException {
		long start = System.currentTimeMillis();
		Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				int a = 0;
				for (long i = 0; i < count; i++) {
					a += 5;
				}
				System.out.println(a);
			}
		});
		thread.start();
		int b = 0;
		for (long i = 0; i < count; i++) {
			b--;
		}
		thread.join();
		long time = System.currentTimeMillis() - start;
		System.out.println("并发:" + time + "ms,b=" + b);
	}

	private static void serial() {
		long start = System.currentTimeMillis();
		int a = 0;
		for (long i = 0; i < count; i++) {
			a += 5;
		}
		int b = 0;
		for (long i = 0; i < count; i++) {
			b--;
		}
		long time = System.currentTimeMillis() - start;
		System.out.println("串行:" + time + "ms,b=" + b + ",a=" + a);
	}

}

执行结果:

50000
并发:2ms,b=-10000
串行:0ms,b=-10000,a=50000

执行相同的任務,对于耗时不大的任務进行拆分多线程执行,可能会适得其反。

创建线程使用是直接向系统申请资源的,对操作系统来说,创建一个线程的代价是十分昂贵的, 需要给它分配内存、列入调度,同时在线程切换的时候还要执行内存换页,CPU 的缓存被清空,切换回来的时候还要重新从内存中读取信息,破坏了数据的局部性。

死锁

两个线程分别获取了锁定,互相等待另一线程解除锁定的现象。发生死锁时,哪个线程都不能执行下去,所以程序就失去了生命性。

避免一个线程同时获取多个锁,避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。

例如:

代码块一:

synchronized (B) {
	//1
     synchronized (A) {
               //.........
     }
}

代码块二:

synchronized (A) {
     //2
     synchronized (B) {
               //.........
     }
}

当线程甲到达1时,线程乙到达2时,两个线程将进入互相等待对方释放对象锁。

资源限制

硬件资源和软件资源

硬件资源

如宽带 硬盘读写 内存 cpu处理速度

软件资源

如数据库连接数、socket连接数等

多线程程序的评量标准

安全性——不损坏对象

暗指对象的字段所取得的值并非预期值

生存性——进行必要的处理

复用性——可再利用类

性能——能快速、大量进行处理

多线程执行不一定比串行执行快

除了上面提到的影响多线程的原因,还有线程有创建和上下文切换的开销

但可以通过调优减少上下文切换的方法有无锁并发编程、CAS算法、使用最少线程和使用协程。因为多线程竞争锁时,会引起上下文切换。

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

并发工具分析

vmstat可以测量上下文切换的次数

用jstack命令dump线程信息,可以查看指定pid的进程里的线程都在做什么,例如:jstack pid,可以使用jstack pid > dump,然后使用:

grep java.lang.Thread.State dump17|awk ‘{print $2$3$4$5}’|sort|uniq -c做统计,可以查看当前运行RUNNABLE、WAITING等状态的数量

jdk有自带工具可以分析线程调度、内存使用,查看java程序的运行情况

参阅书籍:
《Java并发编程艺术》
《Java多线程设计模式》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值