一,进程和线程的联系和区别
参考链接:https://blog.csdn.net/zhou753099943/article/details/51771220
1,两者的概念
a,进程:是操作系统进行资源分配的基本单位,并能在系统中独立运行;
b,线程:是操作系统进行资源调度的基本单位。
2,区别
a,调度:cpu调度和分派的基本单位是线程,而进程作为资源分配的基本单位;如内存就是所谓的资源
b,归属:一个线程只能属于一个进程,但是一个进程可以包含多个线程;
c,创建和销毁:在linux环境下,线程的创建使用fork或者vfork指令,进程使用pthread_create指令进行创建。一个进程的结束,那么进程下面的所有线程都将被销毁。一个线程的结束不会影响其他线程的结束;
d,代价:创建线程的代价比创建进程的代价小很多;
二,java多线程情况下会出现哪些问题
1,常见的问题
a,死锁问题
b,数据一致性的问题
三,讲一下volatile
1,volatile概念
volatile是一种轻量级的同步机制,
其中的轻量级指的是不需要通过加锁的方式。
其中同步指的是同一时刻只能有一个线程访问资源,也就是线程安全
2,volatile的特点
a,可见性(内存一致性):如果一个线程对volatile修饰的变量做了修改,那么其他使用改变量的线程马上可以得知这个修改;
b,禁止指令重排序:在volatile修饰的变量之前的代码不能重排序到该变量之后,之后的代码不能重排序到该变量之前;
c,不具有原子性:也就是说在多线程并发下,操作被volatile修饰的变量,还是引起线程不安全的问题;典型就是i++的问题,10条线程分别操作i++100次,结果并不是1000而是小于1000的;
3,可见性的底层实现原理
写操作:拥有被volatile修饰变量的线程,会监控总线,监控变量是否发生修改,若监控到变量发生修改,那么将内存中变量置为无效;
读操作:判断内存中的变量是否有效,如果无效,那么就重新在主存中获取;如果有效的,那么直接使用;
4,禁止指令重排序的实现原理
a,在每个volatile写操作的前面插入一个StoreStore屏障。StoreStore屏障保证在volatile写之前,保证所有的普通写操作已经任意处理器可见;
b,在每个volatile写操作的后面插入一个StoreLoad屏障。StoreLoad屏障作用volatile写与后面可能有的volatile读/写操作重排序;
c,在每个volatile读操作的后面插入一个LoadLoad屏障。LoadLoad屏障作用避免volatile读与下面的普通读重排序。
d,在每个volatile读操作的后面插入一个LoadStore屏障。LoadStore屏障作用避免volatile读与下面的普通写操作重排序。
五,线程的状态
六,sleep和wait区别
1,sleep()和wait()作用
a,sleep():让线程睡眠,睡眠时间到了,就到runnable状态;
b,wait():让线程等待,等待被唤醒;
2,区别
a,归属:sleep()是Thread类下面的静态方法,wait()是object()下面的实例方法
b,使用位置:sleep是静态方法可以使用在任意一个位置,wait()方法是实例方法,只能 用在同步代码块和同步方法当中;
c,释放锁资源:sleep()方法不会释放锁资源,wait()方法的执行会释放锁资源;
d,作用:sleep()使得线程睡眠进入到timed_waiting状态,wait()使得线程进入到waiting状态
3,注意
sleep()和wait()方法在执行期间,不会占据cpu的资源;
七,创建线程的几种方式?run()和call()的区别
1,三种方式
a,自定义一个类,该类实现Runnable接口,然后重写run(),创建自定义类的对象,然后创建Thread类的对象,将自定义类的对象Thread类的参数,然后调用Thread类对象的start();
b,自定义一个类,该类继承Thread类,然后创建自定义类的对象,调用自定义类的start()。
c,自定义一个类,该类实现Callable接口,然后重写call(),然后创建自定义类的实例,使用线程池来执行自定类对象
1,自定义个一个类MyCall实现Callable接口,重写了call()
MyCall<Integer> mc = new MyCall<>();
ExecutorService executorService = Executors.newFixedThreadPool(线程数目);
2,调用submit(),得到Future对象;
Future<Integer> result = executorService.submit(mc);
3,使用Future对象得到多线程执行的结果
int num = result.get();
2,call()和run()的区别
a,返回值:call()有返回值,是一个
int类型;run()方法没有返回值;
b,使用上:call()需要和线程池结合使用,run()需要和Thread类的实例结合使用;
c,归属上:call()属于Callable接口下面的方法,run()属于Runnable接口下面的方法;
九,多线程环境下会出现什么问题
1,死锁问题:多线下程由于相互争夺资源,而造成相互等待的现象
2,一致性问题:也就是数据不一致问题;比如10个线程,每个线程进行i++操作10次,那么我们理想的结果是100,但实际情况往往会小于100;
十,多线程下,如果对一个数进行叠加要怎么做?
1,加锁,通过synchronized或者ReentrantLock实现
2,通过Atomic原子类实现;
十一,为什么要使用线程池?线程池的实现原理?拒绝策略?常用的线程池有哪些?
1,使用线程池的好处
a,可以减少资源的消耗,避免了频繁的创建和销毁线程,因此减少了资源的消耗
b,提高了响应速度,使用已有的线程来处理,因此省去了线程创建的步骤,那么就提高响应速度;
c,增加了线程管理性,线程的创建,销毁,分配做了管理,因此不需要我们自己去管理线程,因此提高了线程的管理性;
2,线程池的实现原理
第一步:判断corePool核心线程池是否已经满了,若没有满的话,那么直接创建线程,进行处理,如果满了的话,那么就执行第二步
第二步:判断workQueue队里是否满了,若没有满那么直接将线程处理的任务加入到workQueue中,如果工作队列满了的话,那么执行第三步
第三步:判断当前的线程池中的线程数是否超过最大线程数,若果没有超过,直接创建线程进行处理,若超过了,那么执行第四步
第四步:直接使用预先设定好的饱和策略来处理
3,线程池中的参数
a,int corePoolSize:该参数规定了核心线程池所能拥有线程池数量;
b,int maximumPool:该参数规定了整个线程池所能拥有线程的数量;
c,int keepAliveTime:该参数规定了线程存活的时间,如果线程的空闲时间超过了该参数,那么就会被销毁;
d,int timeunit:该参数规定了线程存活时间单位
e,BlockingQueue workQueue:该参数用于存储待处理的线程任务
f,RejectMethod:饱和策略,当线程池中的线程数量已经等于规定的上限,此时接受的线程任务就会被执行该策略;
g,ThreadFactory threadFactory:用于给线程起一个有意义的名字;
4,拒绝策略
a,AbortPolicy:该策略为默认策略,直接抛出拒绝执行异常RejectedExecutionException.
b,CallerRunPolicy(呼叫者政策):使用主线程来处理任务
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
c,DiscardOldestPolicy(丢弃最老的策略):丢弃队列里的对头任务,然后调用线程池来处理当前任务;
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
d,DiscardPolicy(丢弃策略):不进行处理,直接丢弃掉,也不会抛出异常;
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
5,常用的线程池
参考链接:https://blog.csdn.net/hnd978142833/article/details/80253784
a,newFixedThreadPool特点
1,corePoolSize和maximumPoolSize的大小又传入的参数决定,并且两者的大小是一样的;
2,阻塞队列是LinkedBlockingQueue,为无界阻塞队列
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
b,newSingleThreadExecutor特点
1,corePoolsize和maximumPoolSize均为1;
2,阻塞队列是LinkedBlockingQueue,为无界阻塞队列;那么就会maximumPoolSize,
keepAliveTime,rejectMethod为无效参数
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
c,newCachedThreadPool特点
1,corePoolSize大小1,maximum大小Integer.MAX_VALUE
2,阻塞队列为synchronousQueue,该队列为没有容量,一次只能处理一个任务
public static