1.线程和进程
一个进程默认有两个线程,(main,GC)
java真的可以开启线程吗?不可以,java是调用本地方法,底层是c++,java无法直接操作硬件
private native void start();
2.并发和并行
并发(多个线程操作一个资源)
cup单核,多个线程交替使用cup
并行(多个线程可以同时执行)
cup多核,线程池
并发线程的本质就是:充分利用CPU的资源
线程的状态(6种)依据源码
public enum State {
//新生
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待
WAITING,
//超时等待
TIMED_WAITING,
//终结
TERMINATED;
}
wait/sleep区别
1.来自不同的类
wait => Object
sleep=> Thread
2.关于锁的释放
wait会释放锁,sleep不会释放锁
- 释放锁的行为:当一个线程调用了某个对象的
wait()
方法时,它会释放掉该对象上的锁,并进入等待(WAITING)状态。这意味着其他线程可以有机会获取到这个对象锁并执行相应的同步代码块。 - 不释放锁的行为:当一个线程调用了
Thread.sleep(long millis)
方法时,它会暂停执行指定的毫秒数,但这期间它并不会释放掉任何已经持有的锁。这意味着如果该线程持有某个对象锁,并且在该对象锁的同步块中调用了sleep()
,那么其他线程将无法获取到这个对象锁,直到sleep()
方法执行完毕且该线程重新获得执行权。
3.使用的范围不同
wait 必须在同步代码块中
sleep 可以在任何地方
3.Lock锁
Synchronized和Lock区别
1.Synchronized 是内置的java关键字,Lock是一个java类
2.Synchornized 无法判断获取锁的转态,Lock可以判断是否获取到了锁
3.Synchornized 会自动释放锁,Lock必须手动释放锁(不释放会造成死锁)
4.Synchornized 线程1(获取锁,阻塞了),线程2(等待,一直等),Lock锁就不一定会等待(可以尝试获取锁)
5.Synchornized 可重入锁,不可中断,非公平;Lock 可重入锁,可以判断锁,非公平(可以自己设置)
6.Synchornized 适合锁少量的同步代码问题;Lock适合锁大量的同步代码问题
锁是什么,如何判断锁是谁的(下面会后总结)
4.生产者和消费者
面试一般要问:单列模式,排序算法,生产者和消费者问题,死锁。
生产者和消费者问题Synchronized版
生产者和消费者套路(判断等待,业务代码,唤醒)
/**
* 假设Data是一个资源类
* number 共享变量
*/
public class Data {
private int number = 0;
public synchronized void increment() throws InterruptedException {
if (number != 0) {
//等待
this.wait();
}
number++; //生产
System.out.println(Thread.currentThread().getName() + "+1");
//唤醒其它线程
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
if (number == 0) {
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "-1");
this.notifyAll();
}
}
问题存在,若出现A,B,C,D四个线程。出现虚假唤醒
根据官方文档
即上面的资源类中的代码中的 if 应改为 while 判断
/**
* 假设Data是一个资源类
* number 共享变量
*/
public class Data {
private int number = 0;
public synchronized void increment() throws InterruptedException {
while (number != 0) {
//等待
this.wait();
}
number++; //生产
System.out.println(Thread.currentThread().getName() + "+1");
//唤醒其它线程
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
while (number == 0) {
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName() + "-1");
this.notifyAll();
}
}
生产者和消费者问题 JUC 版
-
Condition
因素出Object
监视器方法(wait
,notify
和notifyAll
)成不同的对象,以得到具有多个等待集的每个对象,通过将它们与使用任意的组合的效果Lock
个实现。Lock
替换synchronized
方法和语句的使用,Condition
取代了对象监视器方法的使用。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Data2 {
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
while (number != 0) {
//等待
condition.await();
}
number++; //生产
System.out.println(Thread.currentThread().getName() + "+1");
//唤醒其它线程
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
while (number == 0) {
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName() + "-1");
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
Condition(同步监视器)的特别之处(让线程按规定的循序执行)
在此指定唤醒了监视器2所休眠的线程
5.八锁现象(关于锁的八个问题)
如何判断锁时谁的。永远知道什么是锁。锁到底锁的是谁。
八锁现象——synchronized关键字实战详解_java中的8锁-优快云博客
6.集合类不安全
List集合
并发下ArrayList 并不是线程安全的,如何解决?
public static void main(String[] args) {
//第一种 List<String> list = new Vector<>();
//第二种 List<String> list=Collections.synchronizedList(new ArrayList<>());
//第三种 List<String> list = new CopyOnWriteArrayList<>();
/*
CopyOnWrite 写入时复制 COW 计算机程序设计中的一种优化策越
多个线程调用时 读是国定的,写入时(复制一份 ,避免覆盖,造成数据问题)
为什么CopyOnWrite 比 Vector 好?
Vector是被Synchornized修饰的,而CopyOnWrite用的是Lock锁
*/
}
hashSet底层是什么?
HashSet<Object> set = new HashSet<>();
/**
*
* public HashSet() {
* map = new HashMap<>();
* }
*
* PRESENT就是一个常量值,只是用了map集合的key
*private static final Object PRESENT = new Object();
*
*public boolean add(E e) {
* return map.put(e, PRESENT)==null;
* }
*/
7.Callable
1.可以有返回值
2.可以抛出异常
3.重写的方法不同 call()
8.常用的辅助类
8.1,CountDownLatch
用法:
public static void main(String[] args) throws InterruptedException {
//总数是 10 必须要执行任务的时候在使用
CountDownLatch count = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName());
count.countDown(); //减一
}).start();
}
count.await(); //等到计数器归零后,在向下执行(没归零,就会等待)
System.out.println("执行完毕");
}
可以看成一个减法计数器
8.2,CyclicBarrier
public static void main(String[] args) {
// 7 表示 等待所有线程都到达屏障,可以想象成 +1 要加到7
// 第二个参数,加到7后才能执行
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("搜集成功!");
});
for (int i = 0; i < 7; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName());
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
}).start();
}
/**
* 当然没加到 7 就会一直被等待
*/
}
可以看成一个加法计数器
8.3,Semanphore
public static void main(String[] args) {
// 参数 3 表示 限制的线程数量(看成有三个停车位)
Semaphore semaphore = new Semaphore(3);
//此时以后六辆车来停车
for (int i = 0; i < 6; i++) {
new Thread(()->{
try {
semaphore.acquire(); //获取
System.out.println(Thread.currentThread().getName()+"拿到停车位");
//模拟停靠2s
TimeUnit.SECONDS.sleep(2);
System.out.println("离开停车位");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
semaphore.release();
}
},"A"+i).start();
}
}
semaphore.acquire() 获取,假若满了,等待被释放为止 semaphore.release() 释放,会使当前信号量减一,然后唤醒等待线程 作用: 多个共享资源的互斥使用,并发限流
9.读写锁
public class ReadWriteLockDemo {
public static void main(String[] args) {
Catch aCatch = new Catch();
//多个线程写入
//添加读写多,写入时只允许一个线程操作
for (int i = 0; i < 5; i++) {
new Thread(() -> {
aCatch.write("i", "i");
}).start();
}
//多个线程读
for (int i = 5; i < 10; i++) {
new Thread(() -> {
aCatch.read("i");
}).start();
}
}
}
/**
* 自定义缓存
*/
class Catch {
private volatile Map<String, Object> map = new HashMap<>();
//添加读写多,写入时只允许一个线程操作
//Lock可以解决,Semaphore也可以解决,但是ReadWriteLock锁的粒度更细
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void write(String key, Object value) {
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "写入");
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写完了");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
readWriteLock.writeLock().unlock();
}
}
public void read(String key) {
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "开始读");
Object object = map.get(key);
System.out.println(Thread.currentThread().getName() + "读完了");
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
readWriteLock.readLock().unlock();
}
}
}
读-读(可以共存) 读-写(不可共存) 写-写(不可共存) 我们经常听说的: 独占锁 就是 写锁 共享锁 就是 读锁
10.阻塞队列
BlockingQueue 和 set,List同级
什么情况下使用阻塞队列:多线程并发
四组API:
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞等待 | 超时等待 |
添加 | add | offer() | put() | offer(带参) |
移除 | remove | poll() | take() | poll(带参) |
判断队列首 | element | peek() | - | - |
public static void main(String[] args) {
//指定队列大小3
ArrayBlockingQueue arrayBlockingQueue= new ArrayBlockingQueue(3);
arrayBlockingQueue.add("a");
arrayBlockingQueue.add("b");
arrayBlockingQueue.add("c");
//Exception in thread "main" java.lang.IllegalStateException: Queue full
arrayBlockingQueue.add("d");
System.out.println("-----------------");
arrayBlockingQueue.remove();
arrayBlockingQueue.remove();
arrayBlockingQueue.remove();
//Exception in thread "main" java.util.NoSuchElementException
arrayBlockingQueue.remove();
}
-------------------------------------------------------
带参的offer和poll演示一下
//等待2s后退出
blockingQueue.offer("A",2,TimeUnit.SECONDS);
blockingQueue.pull(2,TimeUnit.SECONDS);
SynchronousQueue(同步队列)
-
其中每个插入操作必须等待另一个线程相应的删除操作,反之亦然。 同步队列没有任何内部容量,甚至没有一个容量(不存储元素,插入一个必须取出再才能插入)
11.线程池
池化技术
事先准备好一些资源,有人要用,就来这里拿,用完后就归还
优化资源使用:如线程池,连接池,内存池,对象池....
线程池的好处:
线程复用,可以控制最大并发数,管理线程
三大方法
public static void main(String[] args) {
/**
* 三大方法
*/
// ExecutorService threadPool = Executors.newSingleThreadExecutor(); //单个线程
//ExecutorService threadPool = Executors.newFixedThreadPool(5); //创建一个固定的线程池大小
ExecutorService threadPool = Executors.newCachedThreadPool(); //可伸缩
try {
//创建线程池后,使用线程池来创建线程
for(int i=0 ;i<100 ;i++){
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName());
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
//完毕线程池
threadPool.shutdown();
}
}
七大参数 (我的其它文章由写过)
在这个里补充一下最大线程数怎么设置(也有调优的思想)
IO密集型: cpu是几核,就设置几核(N+1)
CPU密集型:2*N
自定义线程池:
12.四大函数式接口
Function接口
public static void main(String[] args) {
Function function=new Function<String,String>() {
@Override
public String apply(String o) {
return o;
}
};
System.out.println(function.apply("函数式接口"));
}
Consumer接口
Consumer<String> consumer=new Consumer<String>() {
@Override
public void accept(String o) {
System.out.println(o);
}
};
consumer.accept("你好");
Supplier接口
//只有返回值,没有参数
Supplier<Integer> supplier=new Supplier() {
@Override
public Object get() {
return 1024 ;
}
};
System.out.println(supplier.get());
Predicate接口
Predicate<String> predicate=new Predicate<String>() {
@Override
public boolean test(String str) {
return str.equals("abc");
}
};
System.out.println(predicate.test("abc"));
System.out.println(predicate.test("bc"));
13.Stream流式计算
14.异步回调
Future 设计初衷:对将来的某个事件的结果进行建模
public static void main(String[] args) throws ExecutionException, InterruptedException {
//异步执行
//没有返回值的runAsync 异步回调
CompletableFuture<Void> completableFuture=CompletableFuture.runAsync(()->{
System.out.println(Thread.currentThread().getName());
});
System.out.println("1111");
completableFuture.get();//获取执行结果
}
//有返回值的
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName());
return 999;
});
System.out.println(completableFuture.whenComplete((t,u)->{
System.out.println(t); //正常的返回结果
System.out.println(u); //错误的信息
}).exceptionally((e)->{
System.out.println(e.getMessage());
return -1;
}).get()
);
15.JMM
什么是JMM: Java内存模型,不存在的东西,是概念,约定
16.Volatile
1.保证可见性
2.不保证原子性
3.禁止指令重排
什么是指令重排:我们写的程序,计算机并不是按照我们写的那样去执行的。
源代码->编译器优化的重排->指令并行也可能会重排->内存系统也会重排->执行
17.单例模式
饿汉式:
//饿汉式
public class HungryMan {
//一上来就加载对象,可能会浪费空间
private HungryMan(){}
private final static HungryMan HUNGRY_MAN=new HungryMan();
//对外提供方法
public static HungryMan getInstance(){
return HUNGRY_MAN;
}
}
懒汉式:
public class LazeMan {
private LazeMan() {
}
private volatile static LazeMan lazeMan;
public static LazeMan getInstance() {
/**
* 双重检测锁模式 的 懒汉式 简称 DCL懒汉式
*/
//第一判断解决多线程并发问题
if (lazeMan == null) {
synchronized (LazeMan.class) {
//第二个判断在保证只会生成一个对象
if (lazeMan == null) {
lazeMan = new LazeMan(); //因为不是原子操作,所以加上volatile防止指令重排
}
}
}
return lazeMan;
}
}