目录
一、基础函数
1.wait
线程等待, 此时线程会被阻塞
-
wait()
超时时间为0; -
wait(long timeout)
超时时间为timeout; -
wait(long timeout, int nanos)
超时时间为(timeout++);
PS: 0 <= nanos <= 999999
2.notify
共享对象, 随机被唤醒
- notify()
3.notifyAll
共享对象, 全部被唤醒
- notifyAll()
4.join
等待线程执行终止
- join()
5.sleep
让线程睡眠
-
sleep(long millis)
millis >= 0
-
sleep(long millis, int nanos)
if (nanos >= 500000 || (nanos != 0 && millis == 0)) { millis++; }
PS: millis >= 0, 0 <= nanos <= 999999
6.yield
让出CPU的执行权, 然后处于就绪状态
- 与sleep的区别
- sleep: 调用线程会被阻塞挂起指定时间, 在这个期间线程调度器不会调用该线程
- yield: 线程让出自己剩余的时间片, 并没有被阻塞挂起, 而是处于就绪状态, 线程调度器下一次调用的时候还是有可能调度到当前线程
7.interrupt
中断线程
1.实际没有被中断,只是设置中断标志且仅仅是设置中断标志
, 会继续执行下去
2.对于处于wait、sleep和join的线程, 调用interrupted会抛出InterruptedException异常而返回
-
interrupt()
设置中断标志并返回true -
isInterrupted()
检查线程是否被中断(true:是, false:否),不会
清除中断状态 -
interrupted()
1.静态方法
2.检查线程是否被中断(true:是, false:否),会
清除中断状态
3.获取的是当前线程的中断状态, 并非调用此方法的实例对象的中断标志public static boolean interrupted() { return currentThread().isInterrupted(true); }
二、死锁
-
形成条件
-
互斥
-
不可剥夺
-
请求并持有
-
环路等待
-
-
示例
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (RESOURCE1) {
System.out.println(Thread.currentThread() + "get resource1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "waiting resource2");
synchronized (RESOURCE2) {
System.out.println(Thread.currentThread() + "get resource2");
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (RESOURCE2) {
System.out.println(Thread.currentThread() + "get resource2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "waiting resource1");
synchronized (RESOURCE1) {
System.out.println(Thread.currentThread() + "get resource1");
}
}
});
thread1.start();
thread2.start();
}
- 如何避免
破坏形成条件中其一即可, 其中"请求并持有"以及"环路等待"可以被破坏
三、线程类型
1.守护线程
-
即, daemon线程
-
如何创建
public static void main(String[] args) { Thread thread = new Thread(() -> { }); // 设置为守护线程 thread.setDaemon(true); thread.start(); }
2.用户线程
-
即, user线程
-
默认
PS: JVM进程终止相关
当最后一个非守护线程结束时, 会正常退出, 不管当前是否存在守护线程, 即只要有一个用户线程存在JVM就不会退出
四、ThreadLocal
将共享变量本地副本化
-
线程实际操作的是自己本地内存变量
-
避免了线程安全问题
-
调用后应调用remove()函数
如果线程一直不消亡, 则这些本地变量会一直存在, 可能导致内存溢出 -
不支持继承性
即子线程无法访问父线程的本地变量信息
private static ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { threadLocal.set("hello world"); Thread thread = new Thread(new Runnable() { @Override public void run() { // thread: null System.out.println("thread: " + threadLocal.get()); } }); thread.start(); // main: hello world System.out.println("main: " + threadLocal.get()); }
InheritableThreadLocal
——其子类, 可以解决上述问题
private static ThreadLocal<String> threadLocal = new InheritableThreadLocal<>(); public static void main(String[] args) { threadLocal.set("hello world"); Thread thread = new Thread(new Runnable() { @Override public void run() { // thread: hello world System.out.println("thread: " + threadLocal.get()); } }); thread.start(); // main: hello world System.out.println("main: " + threadLocal.get()); }
五、其他
1.synchronized
-
共享变量内存可见性
-
原子性
-
独占锁
2.volatile
-
共享变量内存可见性
-
非阻塞算法
-
非操作原子性
-
避免指令重排序
共享变量内存可见性: 直接将值刷新到主内存, 即不缓存到本地
3.伪共享
1.多线程下访问同一缓存行的多个变量时则会出现伪共享.
2.这就会导致多线程情况下效率降低, 单线程效率反而会更高
3.什么是缓存行: 缓存与内存交互的数据单位
- 如何避免:
- java8之前填充变量所在缓存行
- java8之后@sun.misc.Contended
- 类——需要添加JVM参数: -XX:-RestrictContented
- 填充宽度默认128, 可自定义参数:-XX:ContentedPaddingWidth
4.锁概念
- 悲观锁
来源数据库, 对所有操作持保守状态
1.认为数据很容易就会被修改
2.所以在数据修改前就会对数据进行加锁, 在整个过程中数据都会处于被加锁状态
- 乐观锁
来源数据库, 认为数据不会那么容易起冲突
1.在访问的时候不会对其进行加锁
2.只有真正对数据进行修改的时候, 才会对数据的冲突与否进行检查
3.通常做法添加version字段进行控制(有点类似CAS操作)
4.较悲观锁, 乐观锁不会产生任何死锁(在数据库中不会使用其提供的锁机制)
- 公平锁
根据线程请求锁的早晚时间进行决定, 来获取锁
1.带来性能开销
2.无公平性需求的情况下, 尽量使用非公平锁
- 非公平锁
运行时闯入, 先来不一定先得到
- 独占锁
任何时候只有一个线程能获得锁(一种悲观锁)
例如: ReentrantLock
- 共享锁
可同时被多个线程持有(一种乐观锁)
例如: ReadWriteLock, 允许一个资源被多个线程进行读操作
- 可重复锁
当一个线程再次获取它自己已经获取的锁时不会被阻塞, 则说明该锁是可重入锁
例如: synchronized其内部锁就是可重入锁
- 自旋锁
在当前线程获取锁时, 如发现已经被其他线程获取, 不立即进行阻塞且不放弃CPU的使用权, 而是进行多次尝试(默认10, -XX:PreBlockSpinsh参数可以设置该值), 尝试结束后如还未获取到则被阻塞挂起
1.使用CPU时间换取线程阻塞和调度的开销
2.可能CPU时间会白白浪费
六、参考资料
- 《Java并发编程之美》