第一篇:多线程学习之概念篇

学习多线程前我们一定要知道一些概念,所以第一篇我们主要介绍概念。知道概念很重要滴!

我会尝试用专业术语加上我自己的语言争取将概念简单化,形象化。先看一下我们要学习的概念分别是什么吧

1.并发与并行

并发(Concurrency):在一个时间段内,多件事情交替执行
并行(Parallelism):多件事情在同一时刻进行

并行举例:饭店, 需要炒出2盘菜, 于是2个厨师, 每一个厨师炒一道菜。
在这里插入图片描述

并发举例:饭店, 需要炒出2盘菜, 但是只有一个厨师, 1和人炒2道菜, 在5道菜 之间来回切换处理。最终呈现给顾客时是两道菜。
在这里插入图片描述

实际上:如果一个系统只有一个CPU,那么多线程和多进程在真实的环境
中是不能做到同时执行的,因为CPU一次只能执行一条指令但是由于CPU
处理的速度非常快,因此给我们的感觉是程序在同时进行的。
现在我们的多核CPU是可以做到并行的。但是我们主要研究的是高并发

2.同步和异步

同步(Synchronus): 方法调用一旦开始,调用者必须等到方法调用返回后,才能继续执行。
异步(Asynchronous): 方法调用开始后,会立马返回,调用者就可以继续后续的操作。

异步同步的概念比较混乱:我们可以这样辅助理解同步就是排队,异步就是并发
在这里插入图片描述

如果不能理解同步和异步,就可以简单理解为同步排队,异步并发。
到了后边我们多线程使用时,就会知道为什么同步是可以保证线程
安全的,其实就是排队等待的缘故。而异步为什么不安全就是我们
后边所谈到的线程安全问题

3.临界区和临界资源

临界资源: 表示一种公共的资源或者共享数据。可以被多个线程使用
          但是一次只允许一个线程进行访问,一旦一个线程使用这个
          资源那么其他线程必须等待。
临界区: 访问临界资源的那些代码都可以称为临界区。

我们往往都喜欢用打印机来描述临界资源,办公室有一台打印机,甲,乙想要使用打印机的话就需要有一个先后顺序,不可能同时打印他们需要的各自的文件,假如可以同时打印的话我们试想打印出来的文件是正确的文件吗?

临界资源就是共享的,但不能同时访问的资源。比如厕所的坑位。。。哈哈哈

4.死锁和活锁

死锁,活锁,以及饥饿都属于线程的活跃性问题,一旦发生这种问题,
线程就会变成不活跃的或者很难执行下去的

死锁发生的必要条件:

1、 互斥使用:   既当资源被一个线程使用(占有)时,别的线程不能使用
2、 不可抢占:   资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有着主动释放
3、 请求和保持:  即当资源请求者在请求其他的资源的同时保持对原有资源的占有
4、 循环等待:   既当存在一个等待循环序列:p1要p2的资源,p2要p1的资源。这就形成了一个等待环路

程序一旦发生死锁就不会执行下去了。
在这里插入图片描述
其实我们可以这样理解就是:互占资源,互不相让,互相影响

死锁模拟(不要发生死锁,但是一定要回写死锁)

//可能你会对今天的代码产生疑问,但是通过以后的学习不会发现这很简单的
public class Test_死锁的模拟 {
	public static void main(String[] args) {
		Object o1 = new Object();
		Object o2 = new Object();
		Test t1 = new Test(o1,o2,true,"线程一");
		Test t2 = new Test(o1,o2,false,"线程二");
		t1.start();
		t2.start();
	}
}
//设置一个线程类
class Test extends Thread{
	private Object o1;
	private Object o2;
	boolean flag;
	public Test(Object o1,Object o2,boolean flag,String name) {
		super(name);
		this.o1 = o1;
		this.o2 = o2;
		this.flag = flag;
	}	
	@Override
	public void run() {
		if(flag) {
			synchronized (o1) {
				try {
					System.out.println(Thread.currentThread().getName()+"获得o1");
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				synchronized (o2) {
					System.out.println(Thread.currentThread().getName()+"获得o2");
				}				
			}		
		}else {
			synchronized(o2) {
				try {
					System.out.println(Thread.currentThread().getName()+"获得o2");
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				synchronized (o1){
				System.out.println(Thread.currentThread().getName()+"获得o1");
				}			
			}		
		}		
	}
}
//执行结果
//线程一获得o1
//线程二获得o2
                  上述代码演示分析
  如果线程一领先抢到CPU,那么走到分支语句就直送true里边的代码
  获得o1对象的锁,此时进行睡眠(sleep睡眠不会释放锁资源)。然后
  线程二抢到CPU执行else后的语句,获得o2对象的锁,然后睡眠,等
  到线程一醒的时候,去获取o2的锁对象时发现此时被线程二占用,同
  理,线程二也获得不了o1对象的锁,因此导致死锁的发生。
活锁:
任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直
重复尝试—失败—尝试—失败的过程。处于活锁的实体是在不断的改变状
态,活锁有可能自行解开

打个比方:好比你要从教室出来,而好一位同学要进教室,此时你们出于礼貌要进行互相谦让,但是对方也是这么想的,因此你们就会撞到一块,撞到后你们立马又进行谦让,你们又相撞了……但是人是聪明的,这种情况一般不会超过三次,但是计算机就不好说了……

5.阻塞和非阻塞

阻塞(Blocking): 线程的一种状态,因为各种原因导致一些线程暂时
不能执行。比如,一个线程占用了临界区资源,而导致其他线程就必须
在临界区中进行等待,这就是一种阻塞。此时如果占用资源的那个线程
一直不释放资源,那么其他线程就只能等待不能正常工作

非阻塞:与阻塞的概念相反,任何一个线程都不会影响其他线程的执行
,所有的线程都在争先恐后的去执行

我们已经介绍了临界区资源是不能让线程同时使用的,因此阻塞的发生也就是在所难免了。就好比一个厕所的坑位一旦一个人占住其他人就不能使用了,人来的越多就会在厕所中拥挤,就类似与这个意思

6.饥饿

饥饿:一个或者多个线程因为种种原因获取不到CPU资源,而导致程序
      不能正常执行下去。当饥饿到一定程度后,该线程执行的操作已
      经没有实际意义时,就称为饿死。
引发饥饿问题的原因有很多,例如线程的优先级差距过大,低优先级的
线程往往由于优先级过低而抢不到CPU资源,导致迟迟不能执行

饥饿死锁代码演示:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Test_饥饿死锁 {

	private static ExecutorService single = Executors.newSingleThreadExecutor();

	public static void main(String[] args) throws ExecutionException,InterruptedException{

		MyCallable task =new MyCallable();

		Future<String> submit = single.submit(task);

		System.out.println(submit.get());

		System.out.println("over");

		single.shutdown();

	}
	public static class AnotherCallable implements Callable<String> {

		@Override
		public String call()throws Exception{
			System.out.println("AnotherCallable执行了");
			return "annother success";
		}
	}
	public static class MyCallable implements Callable<String>{

		@Override
		public String call()throws Exception{
         System.out.println("MyCallable执行了");
         Future<String> submit = single.submit(new AnotherCallable());
			return "success:"+ submit.get();
		}
	}
}
//执行结果
//MyCallable执行了,但是程序没有结束。。。

这是多线程学习前的一些概念,还有很多概念在后期用到的我会在介绍的。可以关注一下我的专栏,边学习边更新,共同加油吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值