JVM中的线程
- 多线程共享进程中的堆和方法区资源
- 每个线程有自己的程序计数器和栈
- 程序计数器记录当前线程要执行的地址,为了cpu下次获取时间片执行线程时知道从哪里执行
JVM线程模型
线程的创建方式
- 继承Thread类
- 实现Runable接口
- 实现Callable<?>
这三种创建线程的差异在哪里
wait()
- 一个线程调用
共享变量
的wait()时,该调用线程会给阻塞,并且释放锁,直到其它线程调用共享变量
的notify()、notifyAll()唤醒返回,或其线程调用该线程
的interrupt()方法,抛异常返回 - 防止虚假唤醒,不停的去测试该线程被唤醒的条件是否满足,不满足继续等待
synchronized(obj){
while(条件不满足){
obj.wait();
}
}
- 同步机制生产者消费例子
public class ProductConsumer {
private static final Integer capacity = 5;
private static final LinkedBlockingQueue queue = new LinkedBlockingQueue(capacity);
public static void main(String[] args) throws InterruptedException, IOException {
Producer producer = new Producer();
Consumer consumer = new Consumer();
Thread p1 = new Thread(producer);
Thread c1 = new Thread(consumer);
Thread c2 = new Thread(consumer);
p1.start();
c1.start();
c2.start();
p1.join();
c1.join();
c2.join();
System.out.println("will read in wait");
System.in.read();
}
private static class Producer implements Runnable {
@Override
public void run() {
try {
while (true) {
synchronized (queue) {
while (queue.size() == capacity) {
System.out.println("producer wait");
queue.wait();
}
long product = System.currentTimeMillis();
queue.add(product);
System.out.println("product=" + product);
queue.notifyAll();
}
TimeUnit.MILLISECONDS.sleep(100);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static class Consumer implements Runnable {
@Override
public void run() {
try {
while (true) {
synchronized (queue) {
while (queue.size() == 0) {
System.out.println("consumer wait");
queue.wait();
}
System.out.println("consumer=" + queue.take());
queue.notifyAll();
}
TimeUnit.SECONDS.sleep(2);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
notify()/notifyAll()
- 需要获取锁才能调用
- 一个线程调用共享对象的notify()会唤醒一个在共享变量上调用wait()后被挂起的线程,唤醒线程是随机的
- 被notify()唤醒后的线程不能马上执行,需要先获取锁
- notifyAll()唤醒在共享变量上所有被挂起的线程,只有竞争到锁的线程才能执行
- notifyAll()只会唤醒调用该方法前调用wait()而被放入共享变量等待集合的线程
join()
- 调用join()线程等待join线程执行完毕后在进行后续处理
- 调用join()的线程被interrupt后会抛InterrupedException
yield()
- 暗示线程调度器当前线程可以让出cpu的使用权,线程调度器可以忽略这个暗示
- 让出cpu使用权后线程并不会阻塞挂起,而是处于就绪状态
线程中断
- interrupt():设置线程的中断标志,仅仅是设置标志,线程会继续执行,如果线程之前调用了wait、join、sleep会抛出InterruptedException
- isInterrupted():检测当前线程是否被中断,不清除中断标志
- interrupted():检测线程是否被中断,如果当前线程被中断,则会清除中断标志,
public static boolean interrupted() {
// 获取当前线程,而不是调用interrupted()的实例对象线程中的中断标志
return currentThread().isInterrupted(true);
}
- 判断中断标志如何输出
Thread t1 = new Thread(()->{
for(;;);
});
t1.start();
t1.interrupt();
Thread.currentThread().interrupt();
System.out.println(t1.isInterrupted());
System.out.println(t1.interrupted());
System.out.println(Thread.interrupted());
System.out.println(t1.isInterrupted());
t1.join();
死锁
- 死锁产生的四个条件:互斥、请求并持有、不可剥夺、环路
- 死锁的原因和申请资源的顺序有关,资源申请有序性可以避免死锁
如何实现一个死锁程序
ThreadLocal
内部结构ThreadLoacl、TheradLoalMap
Thread中的两个变量threadLoacls、inheritableThreadLocals
ThreadLoacl中的set、get、rmove方法实现
子线程、父线程共享数据
网友评论