美文网首页
多线程基础

多线程基础

作者: 木山手札 | 来源:发表于2019-10-27 13:50 被阅读0次

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方法实现
子线程、父线程共享数据

相关文章

网友评论

      本文标题:多线程基础

      本文链接:https://www.haomeiwen.com/subject/rmtuvctx.html