目录
Thread 的 interrupt 函数
public void Thread.interrupt() // 中断线程
public boolean Thread.isInterrupted() // 判断是否被中断
public static boolean Thread.interrupted() // 判断是否被中断,并清除当前中断状态
// 中断响应方法
Thread t1 = new Thread(() -> {
while(true) {
if(Thread.currentThread().isInterrupted()) {
break;
}
}
});
Thread 的 sleep 函数
sleep 的中断异常
有两个线程A、B,当A执行sleep()时,若B调用了A的interrupt(),实质上是设置中断状态,此时A在睡眠,会触发InterruptedException异常,此时需要捕获异常代码段重新设置中断
Thread t1 = new Thread(() -> {
while(true) {
if(Thread.currentThread().isInterrupted()) {
break;
}
// 其他操作
try {
Thread.sleep(1000);
}catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
// 其他操作
}
});
sleep 时不释放资源
线程调用 sleep ,并不会使该线程释放对象锁,而会在睡眠是一直持有,直到睡醒,并执行其余同步块内容,才释放对象锁
测试代码:
public class Test {
public static Object u = new Object();
public static class MyRunnable implements Runnable{
@Override
public void run() {
synchronized (u) {
System.out.println(Thread.currentThread().getName() + " run");
try {
Thread.sleep(1000);
}catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(Thread.currentThread().getName() + " finish");
}
}
}
public static void main(String args[]) throws InterruptedException {
MyRunnable r = new MyRunnable();
Thread t1 = new Thread(r, "t1");
Thread t2 = new Thread(r, "t2");
t1.start();
t2.start();
}
}
测试结果:
Object 的 wait 和 notify 函数
wait 和 notify 要与 关键字 synchronized 配合使用
wait 会释放资源
执行 wait 的线程会释放对象锁,让其他线程持有,使其他线程能唤醒该线程
测试代码:
public class Test {
public static Object u = new Object();
public static class MyRunnable implements Runnable{
@Override
public void run() {
synchronized (u) {
System.out.println(Thread.currentThread().getName() + " run");
try {
u.wait();
}catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(Thread.currentThread().getName() + " finish");
}
}
}
public static void main(String args[]) throws InterruptedException {
MyRunnable r = new MyRunnable();
Thread t1 = new Thread(r, "t1");
Thread t2 = new Thread(r, "t2");
t1.start();
t2.start();
Thread.sleep(1000);
synchronized (u) {
u.notifyAll();
}
}
}
测试结果:
线程池
常用创建线程池方法
public static ExecutorService newFixedThreadPool(int nThreads);
public static ExecutorService newSingleThreadExecutor();
public static ExecutorService newCachedThreadPool();
它们本质上是通过构造 ThreadPoolExecutor 对象,因 ThreadPoolExecutor 是 ExecutorService 的子孙类
newFixedThreadPool() 方法 和 newSingleThreadExecutor() 方法用的是 LinkedBlockingQueue< Runnable >() ,而且没有拒绝策略,意味着当最大线程数满的时候,若有新的任务提交,则会放到等待队列,若提交数度 > 处理数度,等待队列不断增长,最后内存占满
ThreadLocal
多线程访问共享变量可能会线程不安全,若每个线程拥有自己的局部变量,那就不会出现并发问题
Demo(SimpleDateFormat.parse()线程不安全 )
static ThreadLocal<SimpleDateFormat> t1 = new ThreadLocal<>();
public static class ParseDate implements Runnable{
int i = 0;
public ParseDate(int i) {this.i = i;}
public void run() {
try {
if(t1.get() == null) {
t1.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
Date t = t1.get().parse("2015-03-29 19:29:" + i % 60);
System.out.println(i + ":" + t);
} catch(ParseException e) {
e.printStackTrace();
} finally {
t1.remove();
}
}
}
每个 Thread 都有自己的局部变量,它们存放在 threadLocals 中
public class Thread implements Runnable{
...
ThreadLocal.ThreadLocalMap threadLocals = null;
...
}
通过使用 ThreadLocal 的 set() 将要存入的值放入threadLocals 中,当要用的时候使用 get() ,从中获取对应的数据,从而使线程安全
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}