什么是线程和进程 ?
首先我们要知道我们电脑的CPU是支持多进程(process)的,也就是某一时刻支持多个程序一起运行,但实际上CPU是在多个程序快速的切换执行,由于这个时间很快,大大的超出了人的反应时间,所以给我们的感觉就是在某一个时刻是多个程序在并发的运行。那么一个程序同时执行多个任务,而我们把每一个任务就称作一个线程,那么可以同时运行一个线程以上的程序称为多线程程序。
一般而言,进程包括以下三个特征:
1、独立性,进程是系统中独立存在的实体,它可以拥有自己独立的的资源,每一个进程都拥有自己是有的地址空间;
2、动态性,进程与程序的区别在于程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合,在进程中加入时间概念,让进城有了自己的生命周期和各种不同的状态;
3、并发性,多个进程之间可以在系统中并发的执行,相互之间不会互相影响
区别并发性与并行性,并行指在同一时刻多个进程在不同的处理器上同时执行,而并发指的是同一时刻只能有个进程被执行,但处理器在多个进程之间快速的切换实现他们快速轮换执行。
1、多进程与多线程的区别有哪些?
本质区别在于每个进程拥有一整套变量,而线程之间是共享数据(有一定风险),线程之间的数据的共享让线程之间的通信比进程更有效、更容易。
在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)
守护线程的作用是为其他线程的运行提供了便利服务,守护线程最典型的应用就是GC(垃圾回收器,当我们的程序之剩下垃圾回收器作为唯一的线程的时候就表示没有垃圾参数,也就没有垃圾可以回收,那么GC就会自动停止工作)
2.在使用守护线程(也称为后台进程、精灵进程)的时候需要注意以下:
A:在手动设置守护线程的时候必须要在一个线程开启之前对其进行设置,否则会抛出一个IllegalThreadException.
B:在守护线程中产生的线程也是守护线程。
C:并不是所有的应用都是可以分给守护线程来进行服务,比如读写操作或者计算逻辑。因为你并不能确定在所用的用户线程结束之前,守护线程的任务是否结束,如果没有结束,这就导致对应的读写操作或者计算逻辑没有完成。
3.用户线程和守护线程有什么区别?
二者在使用上几乎没有区别,唯一的不同之处在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了,因为没有了被守护者,Daemon Thread就没有工作可以做,也就没有继续运行程序的必要了。
4.Java中定义线程的声明周期分六个阶段:
5.java中提供了让一个线程等待另一个线程完成的方法——join()方法,
如果在某个线程运行过程的时候调用了其他线程的join的方法,那么该线程阻塞,直到调用join 方法的线程执行完毕他才继续执行。
6、由于多线程在切换时候的不确定性,所以当多个线程在对一个数据进行操作的时候,这个时候就有可能出现线程的安全问题,那么如何解决线程安全问题呢?
i):同步的方式利用关键字synchronized
A:同步代码块
语法格式:synchronized(obj){......... 需要同步的代码块....... },obj就是同步监视器,也就是线程在获取执行同步代码块之前必须先要获得同步监视器的锁定
虽然在Java中允许使用任何对象作为监视器对象,但是由于监视器的作用就是阻止多个进程对同一个共享资源的并发操作,所以这里推荐使用对应的共享资源对象作为obj监视器对象
B:同步方法
就是用synchronized修饰对应的方法,不用现实的指定监视器对象,它的监视器对象指的就是this,也就是谁调用该方法这个方法就指谁
synchronized关键字能修饰方法、代码块,但是不能修饰构造器和成员变量
对于可变类(不可变类是线程安全的)的线程安全是以降低程序的运行效率为代价的,为了减少程序带来的负面影响可以采用如下的策略:
a、不要对线程安全类全部的方法都进行同步,只是对那些有资源共享的方法进行同步
b、如果可变类有两种运行环境,单线程环境和多线程环境,这应该为该可变类提供两个版本
一个是单线程不安全版本和线程安全版本(例如jdk中提供的StrinBuffer和StringBuilder两个类,StringBuilder就是单线程不安全版本,StrinBuffer是多线程安全版本)
由于synchronized进行同步解决线程安全问题是隐式的,所以我们并看不出他是什么时候释放对应的监视器锁定呢?
a、当前线程的同步代码或者同步代码块执行完毕的时候,就会释放对应的同步监视器
b、当前线程的同步代码或者同步代码块执行中遇到了break、return终止了代码块和方法的执行,释放同步监视器
c、当前线程的同步代码或者同步代码块执行中遇到了未处理的error或者exception的时候,也是会释放同步监视器
d、当前线程的同步代码或者同步代码块执行中遇到同步监视器对象的wait()方法,则当前线程暂停释放同步监视器
在如下的情况的下是不会释放同步监视器:
a、当前线程的同步代码或者同步代码块执行中程序调用了thread.sleep()、或者 yield()方法暂停当前线程的执行
b、当前线程的同步代码或者同步代码块执行中程序调用了suspend()方法,让线程挂起
ii)、同步锁(Lock)
从Java5开始Java提供了一套功能更强大的线程的同步机制——lock锁接口,显示的定义同步对象实现对应的同步,Lock、R eadWriteLock(读写锁)是两个根接口,并为Lock提供了R eentrantLock(可重入锁)的实现类对象,这也是线程安全中比较常用的锁对象
7、创建线程的有几种方式,有什么不同之处?
目前来说Java提供了三种创建线程的方式:
一、继承thread类,重写run方法
二、实现 runnable接口,重写run方法
三、实现callable接口,重写call方法
8、wait和sleep方法的不同
最大的不同就是,wait会释放锁,而sleep不会释放锁,wait通常用于线程间交互,而sleep通常用于暂停执行
9、线程死锁
死锁:指的是两个或两个以上的锁在争夺线程在执行过程之中,因为争夺资源而造成的一种互相等待的现象,如果没有外力作用,他们将无法推进下去。
产生死锁的原因主要是:
(1)因为系统资源不足,
(2)进程运行推进的速度不合适
(3)资源分配不当
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则
就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 占有且等待:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不可强行占有:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之
一不满足,就不会发生死锁。
死锁的解除与预防:
理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和
解除死锁。所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确
定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态
的情况下占用资源。因此,对资源的分配要给予合理的规划