深刻理解java并发编程基础篇(一)-------并发编程相关概念

1、前言

  拖了好久的并发编程,今天会开始第一篇,主要分为俩大部分进行学习:分为基础篇以及进阶篇,下面就开始基础篇的学习。java

2、并发编程的相关概念

2.1.同步(Sync)与异步(Async)

2.2.1 同步(Sync)

  所谓同步,就是发出一个功能调用时,在没有获得结果以前,该调用就不返回或继续执行后续操做。
  根据这个定义,Java中全部方法都是同步调用,应为必需要等到结果后才会继续执行。 咱们在说同步、异步的时候,通常而言是特指那些须要其余端协做或者须要必定时间完成的任务。简单来讲,同步就是必须一件一件事作,等前一件作完了才能作下一件事。编程

2.2.2 异步(Async)

  异步与同步相对,当一个异步过程调用发出后,调用者在没有获得结果以前,就能够继续执行后续操做。 当这个调用完成后,通常经过状态、通知和回调来通知调用者。对于异步调用,调用的返回并不受调用者控制。多线程

以下图示:并发

2.2.并发(Concurrency)和并行(Parallelism)

  并发和并行是两个很是容易被混淆的概念。他们均可以表示两个或者多个任务一块儿执行,可是侧重点有所不一样。并发偏重于多个任务交替执行,而多个任务之间有可能仍是串行的,而并行是真正意义上的“同时执行”,以下图:异步

  实际上,若是系统内只有一个CPU,而使用多进程或者多线程任务,那么真实环境中这些任务不多是真实并行的,毕竟一个CPU一次只能执行一条指令,在这种状况下多进程或者多线程就是并发的,而不是并行的(操做系统会不停地切换多任务)。真实的并行也只可能出如今拥有多个CPU的系统中(好比多核CPU)。ide

2.3.临界区

  临界区表示公共资源或是共享数据,能够被多个线程使用。可是每次只能有一个线程使用它,一旦临界区的资源被占用,其余线程就必须等到资源释放后才能继续使用该资源。在Java程序开发中,对于这样的资源通常都须要作同步的操做,例以下面的这段代码,用的就是synchronized关键字来对临界区资源进行同步:学习

package com.MyMineBug.demoRun.test;

/** * * @author 18360 * */
public class SyncTest implements Runnable {

	// 临界区资源
	public static SyncTest instance = new SyncTest();

	@Override
	public void run() {
		synchronized (instance) {

		}
	}
	
	public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new SyncTest());
        Thread t2 = new Thread(new SyncTest());
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }

}

复制代码

2.4. 阻塞(Blocking)和非阻塞(Non-Blocking)

  阻塞和非阻塞一般用来形容不少线程间的相互影响。好比一个线程占用了共享资源,那么其余全部须要这个资源的线程就必须在这个临界区中等待。等待会致使线程挂起,这种状况就是阻塞。此时,若是占用资源的线程一直不肯意释放资源,那么其余线程阻塞在这个临界区上的线程都不能工做。spa

  非阻塞的意思与之相反,它强调没有一个线程能够妨碍其余线程执行,全部的线程都会尝试不断向前执行操作系统

2.5.死锁(Deadlock)、饥饿(Starvation)和活锁(Livelock)

2.5.1 死锁(Deadlock)

  死锁通常是指两个或者两个以上的线程互相持有对方所需的资源,而且永远在等待对方释放的一种阻塞状态。例若有两个线程A和B同时共享临界区的资源C,当A占用C时,B处于阻塞状态,然而A的释放须要用到B的资源,这样一来,就变成了A一直在等待B,B也一直在等待A,互相之间永远在等待对方释放的状态。线程

如下是死锁的简单例子:

package com.MyMineBug.demoRun.test;

public class Demo1 {

	public static String obj1 = "obj1";
	public static String obj2 = "obj2";

	public static void main(String[] args) {
		Thread a = new Thread(new Lock1());
		Thread b = new Thread(new Lock2());
		a.start();
		b.start();
	}

}

	class Lock1 implements Runnable {
	
		@Override
		public void run() {
			try {
				System.out.println("Lock1 running");
				while (true) {
					synchronized (Demo1.obj1) {
						System.out.println("Lock1 lock obj1");
						Thread.sleep(3000);// 获取obj1后先等一下子,让Lock2有足够的时间锁住obj2
						synchronized (Demo1.obj2) {
							System.out.println("Lock1 lock obj2");
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
	
		}
	
	}
	
	class Lock2 implements Runnable {
	
		@Override
		public void run() {
			try {
				System.out.println("Lock2 running");
				while (true) {
					synchronized (Demo1.obj2) {
						System.out.println("Lock2 lock obj2");
						Thread.sleep(3000);
						synchronized (Demo1.obj1) {
							System.out.println("Lock2 lock obj1");
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
	
		}
	
	}


复制代码

运行结果以下:

通常来讲,死锁的发生是因为程序的设计不合理致使,并且死锁很难解决,最好的方式就是预防.

2.5.2 饥饿(Starvation)

  饥饿是指某一个或者多个线程由于种种缘由没法得到所需的资源,致使一直没法执行。好比它的线程优先级过低,而高优先级的线程不断抢占它所需的资源,致使低优先级资源没法工做。

2.5.3 活锁(Livelock)

  活锁的状况是线程一种很是有趣的状况,在生活中咱们可能会碰到这样的状况,那就是出门的时候可能会遇到有人要进门,你打算让他先进门,他又打算让你先出门,结果,两我的都互相退后了,而后你打算先出门时对方也向前一步,来来回回就一直卡在门口。固然,这种事情正常人很快就能解决,但若是是线程碰到就没那么幸运了。

  若是两个线程占用着公共的资源,而且秉承着 “谦让” 的原则,主动把资源让给他人使用,你让我也让,这样就形成资源在两个线程间不断跳动但线程之间都拿不到资源的状况,这样的状况就是活锁了。

  若是以为还不错,请点个赞!!!

  Share Technology And Love Life

相关文章
相关标签/搜索