并发和并行的概念
并发:在同一时间段上同时执行,是通过一个cup进行切换完成任务
并行:在同一时间点上同时执行,是通过多个cpu同时干活完成任务
并发和并行都是通过线程实现

线程是什么,进程是什么
进程:运行中的应用程序;如qq,微信等软件
线程:运行程序中的一个顺序控制流程,也是CPU中的最小调度单位;
线程使用方式
Thread:创建方式
class ThreadA extends Thread {
@Override
public void run() {
System.out.println("Thread");
}
}
Thread和Runnable区别:
1、Thread是一个类,Runnable是一个接口
2、Thread底层会真正创建一个线程,但Runnable不会去创建线程,它只是个任务,如果需要执行任务还是需要Thread去实现多线程来完成这个任务
Runnable:创建方式
class RunnableB implements Runnable {
@Override
public void run() {
System.out.println("Runnable");
}
}
Runnable和Callable区别:
1、Runnable的任务是没有返回值,而Callable是可以有返回值
2、Callable是支持异常往外抛,而Runnable是不支持异常往外抛,只能在run方法内捕获异常
Callable:创建方式
class CallableC implements Callable {
@Override
public Object call() throws Exception {
System.out.println("Callable");
return "Callable";
}
}
线程池:创建方式
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
// 核心池大小
taskExecutor.setCorePoolSize(5);
// 最大线程数
taskExecutor.setMaxPoolSize(10);
// 队列程度
taskExecutor.setQueueCapacity(100);
// 线程空闲时间
taskExecutor.setKeepAliveSeconds(60);
// 线程前缀名称
taskExecutor.setThreadNamePrefix("syncExecutor--");
// 该方法用来设置 线程池关闭 的时候 等待 所有任务都完成后,再继续 销毁 其他的 Bean,
// 这样这些 异步任务 的 销毁 就会先于 数据库连接池对象 的销毁。
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
// 任务的等待时间 如果超过这个时间还没有销毁就 强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
taskExecutor.setAwaitTerminationSeconds(60);
// 线程不够用时由调用的线程处理该任务
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
真正实现多线程的方式只有继承Thread一种,其他三种底层都是通过Thread来实现的
线程的生命周期
1、new:初始化状态【当我们new出一个线程时,该线程在未调用start方法时处于新建状态未执行】
2、runnable:
start:就绪状态【只是通知底层操作系统,我已经就绪状态,可以分配时间片给我】
runnable:运行状态【当线程拿到cpu执行权是执行任务】
3、timed_waiting:限期等待【当我们调用了Thread.sleep方法后,临时等待】
4、waiting:无限期等待【当我们调用了Thread.wait方法后,处于无限等待,需要调用notify或notifyAll方法唤醒】
5、blocked:阻塞状态【该状态只有遇到Synchroniezd的时候,切没有抢到锁的时候,处于阻塞状态(因为Synchroniezd方法中调用了wait后它是不会释放锁资源的)】
6、terminated:终止状态【该线程执行完毕结束】

怎么停止线程
stop:暴力停止,强制终止线程任务,不建议使用
interrupt:更改中断标记,唤醒处于阻塞状态下的线程
interrupted:返回中断标记,然后复位
isinterruputed:返回中断标记
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new InterruptExample());
t1.start();
Thread.sleep(500);
//Main线程来决定t1线程的生死
t1.interrupt();//发送一个中断信号, 中断标记变为true
// 让当前线程中断状态的复位。
// Thread.interrupted();
}
public void run() {
//如果让线程友好结束,只有当前run方法中的程序知道.
//Thread.currentThread().isInterrupted()获取中断状态
try {
Thread.sleep(3000);
System.out.println("张三");
} catch (InterruptedException e) { //中断标记变为false
e.printStackTrace();
System.out.println(e);
System.out.println("中断线程interrupt");
//TODO 临时保存一些数据.
//把中断标记修改为true
return;
}
System.out.println(Thread.currentThread().getName()+"--");
}
线程在使用过程中又产生了那些问题
线程问题的本质:并发
线程解决问题:加锁
解决安全性问题-锁
安全性问题分为:可见性问题,有序性问题,原子性问题
Synchronized使用方式
1、普通同步方法,锁的当前实例对象this
2、静态同步方法,锁的当前类的class对象
3、同步方法块,锁的括号里面的对象
synchronized的原理
除了常用的Synchronized锁还有那些锁
1、乐观锁:乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作。乐观锁基本上都是通过CAS操作实现。CAS 是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败。【CAS】
2、悲观锁:悲观锁就是悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会 block 直到拿到锁。
3、自旋锁:自旋锁原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋),等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。
4、非公平锁:加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待【Reentrantlock】
5、公平锁:加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得【Reentrantlock】
6、互斥锁:在编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
7、可重入锁:可重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。在 JAVA 环境下 ReentrantLock 和 synchronized 都是 可重入锁。
8、读写锁:Java 提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,如果没有写锁的情况下,读是无阻塞的,在一定程度上提高了程序的执行效率。读写锁分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥
9、共享锁:共享锁则允许多个线程同时获取锁,并发访问 共享资源,如:ReadWriteLock。共享锁则是一种乐观锁,它放宽了加锁策略,允许多个执行读操作的线程同时访问共享资源。【ReadWriteLock】
10、独占锁:独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock 就是以独占方式实现的互斥锁。独占锁是一种悲观保守的加锁策略,它避免了读/读冲突,如果某个只读线程获取锁,则其他读线程都只能等待,这种情况下就限制了不必要的并发性,因为读操作并不会影响数据的一致性。【Synchronized】
11、分段锁:分段锁也并非一种实际的锁,而是一种思想 ConcurrentHashMap 是学习分段锁的最好实践
Reentrantlock与Synchronized区别
共同点:
1、都是用来协调多线程共享对象、变量的访问
2、都是可重入锁,同一线程可以多次获得同一个锁
3、都保证了可见性和互斥性
不同点:
1、ReentrantLock显示的获得、释放锁,synchronized隐式获得释放锁
2、ReentrantLock可响应中断、可轮回,synchronized是不可以响应中断的,为处理锁的不可用性提供了更高的灵活性
3、ReentrantLock是API级别的,synchronized是JVM级别的
4、ReentrantLock可以实现公平锁,和非公平锁
5、底层实现不一样,synchronized是同步阻塞,使用的是悲观并发策略,lock是同步非阻塞,采用的是乐观并发策略
6、Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现。
7、synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁。
8、Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断。
9、通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
10、Lock可以提高多个线程进行读操作的效率,既就是实现读写锁等。