多线程

133 阅读5分钟

一、什么是进程与线程?以及为什么要使用多线程?

进程

	可以简单的理解为正在运行的程序,是线程的容器
	进程是操作系统资源分配的最小单元。一个进程拥有的资源有⾃⼰的堆、栈等资源(例如jvm进程)

线程

	从属于进程,一个进程可以包含很多个线程
	线程是操作系统能够进行运算调度的最小单元。是进程中实际运行的单位。一个进程中每个线程执行不同的任务 。
		每个java程序至少包含两个线程
			一个main线程
		        一个垃圾回收线程 
                            

为什么使用多线程

  使用多线程主要是提高CPU的利用率,加快任务执行效率

二、并发与并行概念

并发Concurrent

	同一时刻有多个任务(指令)在单个Cpu上交替执行
		针对单核CPU提出

并行parallel

	同一时刻有多个任务(指令)在多个CPU上同时执行
		针对多核CPU提出

三、多线程的同步与异步

同步

	按照顺序一步步执行代码,同步指的是线程之间的执行顺序是按照代码的顺序依次执行的,
            即一个线程需要等待另一个线程完成某个操作后才能继续执行。在同步模式下,线程之间共享数据,需要使用锁等机制来确保数据的一致性和安全性。

异步Future

	代码获取结果需要分开执行,异步指的是线程之间的执行是相互独立的,不需要等待其他线程的完成。
            在异步模式下,线程之间不共享数据,每个线程有自己的独立工作空间,通过消息队列或回调函数等方式来进行线程间的通信和数据传递。
            

四、创建线程的三种方式以及优缺点及注意事项

1.继承Thread类:

通过继承Thread类,并重写其run()方法来创建线程。然后使用该子类的实例调用start()方法启动线程。
优点:
简单直接,代码结构清晰。
缺点:
因为Java不支持多重继承,继承了Thread类的子类不能再继承其他类。线程与代码的耦合紧密,任务与线程的关系较为固定。

2.实现Runnable接口:

  定义一个类实现Runnable接口,并实现其run()方法。然后创建Thread的实例,将Runnable实例作为参数传递给Thread的构造方法,并调用start()方法启动线程。
优点:
线程与任务之间解耦,任务可以通过Runnable接口独立定义。
可以实现资源共享,多个线程可以共享同一个Runnable实例。
缺点:
代码稍微复杂一些,需要显式地创建Runnable实例,再传入Thread的构造方法。

3.实现Callable接口:

  类似于实现Runnable接口,但是run()方法没有返回值,而是使用Callable的call()方法执行任务,并返回一个结果。
优点:
可以获取线程执行任务的返回值。
可以抛出异常,方便错误处理。
缺点:
创建线程时需要使用ExecutorService的submit()方法调度任务。


1.使用目的:拿到线程的执行结果
2.创建方式: 搭桥
3.获取结果: get(阻塞方法:会阻塞程序的运行)


4.start与run的区别 :

start会启动线程,run不会只是普通的调用方法

5.线程不允许多次启动

6.线程是由cpu来调度的,start之后,只是进入就绪状态

Thread类中的方法

	sleep                  线程休眠
            
	Thread.currentThread   返回当前线程对象
            
获取线程名称
    
	静态方法Thread.currentThread.getName()
            
	成员方法getName
            
设置线程名称
    
	静态方法Thread.currentThread.setName()
            
	成员方法setName
            

五、线程操作共享数据时的安全问题

当多个线程去访问一个公共的资源时,就会出现资源数值错误
    
问题出现的原因:
    存在非原子操作
    
什么叫原子操作:
	所有操作要么一起执行,要么一起失败
解决方法:加锁
	synchronized关键字同步方法
	synchronized同步代码块
            Lock锁
            
synchronized锁
synchronized锁实现线程间的同步,保证原子操作
        synchronized的缺陷
	效率低
		锁的释放情况少(同步代码块中执行的非常耗时的操作)
		试图获取锁时不能设定超时
	不够灵活
		不能自己控制加锁和解锁的时机
		每一个锁仅有单一的条件(某个对象)
		无法知道是否成功获取到锁
                    
                  

同步锁:锁的是方法
互斥锁:线程互斥 同时只能有一个线程执行加锁的方法
非公平锁:线程抢锁的概念是随机的
隐式锁:看不到锁
可重入锁: 同一个线程抢到锁之后可以再次获取该锁
加锁的步骤:
1.首先所有线程参与抢锁
2.成功抢到的线程执行加锁的方法
3.释放锁

                    
显示锁 Lock锁
	        ReetrantLock
		可重入锁
                    

死锁

死锁就是指两个或两个以上的线程在执行过程中,由于竞争资源产生的互相等待资源释放的现象
具体原因
	互斥条件:一个资源只能被一个线程占有,当这个资源被占有后其他线程就只能等待
	不可剥夺条件:当一个线程不主动释放资源时,此资源一直被拥有线程占有
	请求并持有条件:线程已经拥有了一个资源后,又尝试请求新的资源
	环路等待条件:产生死锁一定是发生了线程资源环形链
解决死锁
	改变资源的环路等待,顺序获取锁资源