-
进程
-
系统中一个运行的程序就是一个进程,每个进程都有自己的内存空间和系统资源
-
进程是操作系统分配资源的基本单位
-
-
线程
-
也叫做轻量级进程,一个进程中有多个线程,这些线程共享一个进程中的资源
-
线程是CPU调度的基本单位
-
-
管程
-
Monitor(监视器),锁,是一种同分机制,保证同一时间只有一个线程可以访问同步的代码
-
Monitor对象和Java对象同一时间创建并销毁。底层由C++实现
-
-
用户线程:系统的工作线程,会完成程序需要完成的业务
-
守护线程:为用户线程服务的后台线程,若用户线程全部结束,就只剩下守护线程,守护线程没有服务的对象,守护线程随着JVM结束运行
-
isDaemon():判断是否为守护线程
-
setDaemon(true/false):true表示设置为守护线程,false表示设置为用户线程;但是需要在strat()之前设置
-
新建
-
当使用new创建一个线程但没调用start()时,此时的状态是新建状态
-
-
就绪
-
调用start()后,线程就进入就绪状态,但此时不会立即执行run(),需要等待获取CPU资源(线程运行的顺序可能和创建的顺序不一致)
-
-
运行
-
当线程获取到CPU时间片后,就会进入运行状态,开始执行run()
-
-
阻塞
-
当遇到以下情况,线程就会进入阻塞状态
-
调用sleep(),使线程睡眠
-
调用wait(),使线程等待
-
当线程去获取同步锁,锁正在被其他线程持有
-
调用阻塞式IO方法
-
调用suspend(),挂起线程
-
-
-
死亡
-
当run()正常执行结束或抛出异常都会使线程进入死亡状态
-
-
创建线程重写run(),启动线程用start()
-
当start()开始一个线程后,线程就进入就绪状态,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行,但并不意味着线程就会立即运行;当cpu分配给它时间时,才开始执行run()方法(如果有的话)
-
start()调用run(),run()被重写,run()中包含的是线程的主体
-
synchronized是自动获得锁和自动解锁
-
synchronized实现同步,Java中每个对象都可以作为锁
-
对于普通方法,锁是当前实例
-
对于静态方法,锁是当前类的Class对象
-
对于同步代码块,锁是synchronized括号配置的对象
-
-
synchronized本身是非公平锁
-
使用this.wait()进行等待
this.wait();
-
synchronized可重入锁
Object o = new Object();
new Thread(() -> {
synchronized (o) {
System.out.println(Thread.currentThread().getName() + " 外层");
synchronized (o){
System.out.println(Thread.currentThread().getName() + " 中层");
synchronized (o){
System.out.println(Thread.currentThread().getName() + " 内层");
}
}
}
},"线程A").start();
-
常用方法:
-
this.wait():把当前线程挂起
-
this.notify():唤醒阻塞的线程
-
this.notifyAll:唤醒其他所有的阻塞的线程
-
-
lock需要手动加锁和手动解锁,一般使用Lock的实现类ReentrantLock创建锁(可重入锁)
-
ReentrantLock()里面参数可填true或false,默认false
-
true表示公平锁,使得每个线程都会运行,但是效率低
-
false表示非公平锁,效率高,但是会出现线程饿死的情况
-
private final Lock lock = new ReentrantLock();
-
使用lock的newCondition()创建Condition对象,再进行condition.await()进行等待
private final Condition condition = lock.newCondition(); condition.await();
-
lock可重入锁
ReentrantLock lock = new ReentrantLock();
new Thread(() -> {
try {
lock.lock();//加锁
System.out.println(Thread.currentThread().getName() + " 外层");
try {
lock.lock();//加锁
System.out.println(Thread.currentThread().getName() + " 内层");
}finally {
lock.unlock();//解锁
}
}finally {
lock.unlock();//解锁
}
},"线程1").start();
-
常用方法:
-
condition.await():把当前线程挂起
-
condition.signal():唤醒阻塞的线程
-
-
使用this.wait()或者condition.await()进行等待,当被唤醒时,由于等待是在哪里睡,就在哪里醒,因此在等待时的条件不能是if,应是while,此时就会一直判断
while (number != 0) {
this.wait();
}
while (number != 0) {
condition.await();
}
volatile和synchronized
-
volatile是线程同步的轻量级实现,volatile性能比synchronized好;volatile只能修饰变量,synchronized能修饰方法,代码块
-
多线程访问volatile不会阻塞,而synchronized会阻塞
-
volatile能保证数据的可见性,但是不能保证原子性;synchronized既可以保证原子性,也可以保证可见性
-
volatile解决的是变量在多个线程之间的可见性,synchronized解决的是多线程访问公共资源的同步性
-
使用Vector
Listlist = new Vector<>();
-
使用Collections工具类的synchronizedList()
Listlist = Collections.synchronizedList(new ArrayList<>());
-
使用CopyWriteArrayList(写时复制技术)
-
写时复制技术
-
当有多个线程请求同一资源时,他们会共同获取相同的引用指向相同的资源,若某个线程试图修改内容时,系统会复制一份副本给该线程,该线程修改完成后,将副本和原来的资源进行合并
-
-
ListHashSet线程不安全问题list = new CopyOnWriteArrayList<>();
-
使用CopyOnWriteArraySet(写时复制技术)
SetHashMap线程不安全问题set = new CopyOnWriteArraySet<>();
-
使用ConcurrentHashMap
MapCallable创建线程map = new ConcurrentHashMap<>();
-
Callable和Runnable的区别:Callable有返回值,Runnable没有返回值
-
创建线程时,new Thread()里需要传入Runnable,但不支持Callable,而FutureTask实现了Runnable接口,在new Future()就可以传入Callable
FutureTaskfutureTask = new FutureTask<>(() -> { System.out.println(Thread.currentThread().getName()); return 1111; }); new Thread(futureTask,"线程A").start(); System.out.println("线程A返回值:" + futureTask.get());//可获取Callable返回值
-
FutureTask(未来任务),异步并行执行
-
主线程让子线程去完成一个任务,子线程可能比较耗时,启动子线程开启执行任务后,主线程就去做其他事,忙完其他事后再去获取子线程执行的结果
-
-
减少计数:每个线程执行一次就减少一个数量级,减少到0的时候再继续执行之后的代码
CountDownLatch countDownLatch = new CountDownLatch(5);//设置初始值
for (int i = 1; i <= 5; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 号线程执行了");
countDownLatch.countDown();//计数减1
}, String.valueOf(i)).start();
}
countDownLatch.await();//值没有变为0的时候就一直等待,值为0的时候才执行下面的代码
System.out.println(Thread.currentThread().getName() + " 所有线程执行完毕");
CyclicBarrier
-
循环栅栏,和减少计数相反,当线程执行的个数达到设置的个数时,再执行之后的代码
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,() -> {
System.out.println("所有线程执行完毕");
});
for (int i = 1; i <= 7; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "号线程执行了");
cyclicBarrier.await();//等待,当七个线程执行完成后,才会执行CyclicBarrier里的代码
} catch (Exception e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
Semaphore
-
信号灯:多个线程进入有限个数的资源,例如:6辆车停到3个停车位
Semaphore semaphore = new Semaphore(3);//许可数量
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
try {
semaphore.acquire();//抢到车位
System.out.println(Thread.currentThread().getName() + " 抢到车位");
TimeUnit.SECONDS.sleep(new Random().nextInt(5));//设置随机停车时间(5s内)
System.out.println(Thread.currentThread().getName() + " ---离开停车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();//释放停车位
}
},String.valueOf(i)).start();
}
读写锁
-
写锁(独占锁),读锁(共享锁)
-
一个资源可以被多个读线程访问,或可以被一个写线程访问
-
不能同时存在读写线程,读和读共享、读和写互斥、写和写互斥
-
两个线程执行的时候并持有对方的锁,两个线程都处于等待对方释放锁的情况,造成死锁
Object a = new Object();
Object b = new Object();
new Thread(() -> {
synchronized (a){
System.out.println(Thread.currentThread().getName() + "持有锁a,试图获取锁b");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b){
System.out.println(Thread.currentThread().getName() + "获取到锁b");
}
}
},"线程1").start();
new Thread(() -> {
synchronized (b){
System.out.println(Thread.currentThread().getName() + "持有锁b,试图获取锁a");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (a){
System.out.println(Thread.currentThread().getName() + "获取到锁a");
}
}
},"线程2").start();
锁的区别
-
无锁:多线程前方多资源,数据不一致
-
独占锁synchronized和ReentrantLock
-
写和写、读和写、读和读都是独占的,同一时刻只能有一个线程访问
-
读和读不能共享,效率低
-
-
读写锁ReentrantReadWriteLock
-
写和写、写和读是独占的,读和读可以共享,提高性能
-
缺点:①会造成锁饥饿;②写的时候可以读,但读的时候不能写
-
读写锁降级:把写锁降级为读锁
-
过程:①获取写锁;②获取读锁;③释放写锁;④释放读锁
-
-
-
在队列的头部有一个线程,负责把数据添加到队列中;若队列满,再添加数据时队列就处于阻塞状态
-
在队列的尾部有一个线程,负责把数据从队列中取出来;若队列空,再取出数据时队列就处于阻塞状态
-
三种线程池:
-
一池多线程
-
一池一线程
-
一池可扩容
-
-
三种线程池平时都不使用,而是自定义线程池,线程池的三种创建方法底层都是new ThreadPoolExecutor()
-
自定义线程池:创建ThreadPoolExecutor()的七个参数
-
int corePoolSize:常驻线程数量
-
int maximumPoolSize:最大线程数量
-
long keepAliveTime:线程存活时间
-
TimeUnit unit:线程存活时间的单位
-
BlockingQueue
workQueue:阻塞队列 -
ThreadFactory threadFactory:线程工厂,用于创建线程
-
RejectedExecutionHandler handler:拒绝策略
-
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2,
5,
2L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
try {
for (int i = 1; i <= 10; i++) {
executor.execute(() -> {
System.out.println(Thread.currentThread().getName() + "线程执行了");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
executor.shutdown();
}
分支合并框架
-
利用多线程实现类似分治算法
//异步调用,没有返回值 CompletableFuturefuture1 = CompletableFuture.runAsync(() -> { System.out.println(Thread.currentThread().getName() + "future1"); }); future1.get(); System.out.println(); //异步调用,有返回值 CompletableFuture future2 = CompletableFuture.supplyAsync(() -> { System.out.println(Thread.currentThread().getName() + "future2"); return 1111; }); future2.whenComplete((t,u) -> {//当完成任务时执行 System.out.println("t:" + t + ",u:" + u);//t为方法返回值,u为异常 }).get();



