java-多线程

多线程

概述
  • 在一个线程中有多条执行路径

  • 进程:正在执行的程序,他代表着应用程序的执行区域

  • 线程:进程执行路径,就是进程中一个负责程序执行的控制单元。

    ​ 线程总是属于某个进程,进程中的多个线程共享进程的内存

关于多线程的问题
  • jvm启动时多线程还是单线程的,为什么?

    多线程的,他至少启动了两个线程(主线程和垃圾回收线程)垃圾回收机制这个线程不可能是在程序执行完毕后才启动的,否则的话,我们的程序很容易出现内存溢出

  • 调用start方法和run方法区别?

    调用start方法后,线程进入就绪状态,此时线程对象仅有执行资格,还没有执行权。当该线程对象抢到了执行权的时,方可调用run方法,当run方法执行完毕后,线程死亡,不能复生。

  • 线程的随机性的导致原因?

    在同一时刻,CPU只能执行一个程序,这个多线程的程序其实是CPU的高速切换造成的。

  • 什么时候使用多线程?以及创建线程的目的?

    多线程的引入是为了解决现实生活中的需求的,提高解决问题的效率。

    当许多对象要对同一有限资源进行操作的时候,我们就要使用多线程

  • 线程状态的内容和每一个状态的特点?

    创建线程对象后,并对这个对象进行了一些初始化工作,当调用了start方法后,这个状态局有了执行资格,但是此时还未获得执行权,进入到了就绪状态。当抢到执行权后进入了运行状态,此时该线程既有了执行资格,又有了执行权。当调用run方法后,此线程进入了死亡状态。当然你也可以调用Stop方法令其强制死亡。在运行状态的时候,如果该线程调用了sleep或者wait等方法后,他会进入到阻塞状态,此时这个对象释放了执行资格和执行权,当它的sleep或者wait等方法后,亦或者调用了notify方法,该线程被唤醒,又进入了就绪状态,如此周而复始。

创建线程的方式
  • 继承Thread类

    • 定义一个类继承Thread类
    • 重写Thread类中的run()方法,run()方法里边是多线程要运行的代码;
    • 创建定义的那个类的对象
    • 调用start()方法开启线程,执行run()方法里的内容。
    package com.xtslife.thread;
    
    /**
     * @author 小涛
     * @create 2019-05-28  18:55
     */
    public class FirstThread extends Thread{
        @Override
        public void run() {
            for (int i= 0;i<=10;i++){
                System.out.println("线程"+getName()+"正在运行:"+i);
            }
        }
    
        public static void main(String[] args) {
            FirstThread t1 = new FirstThread();
            FirstThread t2 = new FirstThread();
            t1.start();
            t2.start();
        }
    }
    

    线程的声明周期:创建-----阻塞-----运行----死亡

  • 实现Runnable接口

    • 定义一个类实现Runnable接口
    • 重写Runable接口中的run()方法,run()方法里是多线程要运行的代码
    • 创建定义的那个类的对象,并将其作为参数放置于Thread类的对象里。线程的任务都封装在Runnable接口子类对象的run方法中,所以要在线程对象创建时就必须明确要运行的任务。
    • 调用start()方法启动线程,执行run()方法里的内容
    • 总结:两种方法的比较:实现Runnable接口,将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务的封装成了对象,避免了java中单继承的局限性。
    多线程的安全问题
    • 产生的原因
      • 线程访问的延迟
      • 线程的随机性
    • 线程安全问题表现?原因?解决思想?解决具体的体现
      • 当以个线程对象在执行run方法是的某一操作,其他线程进来了,并发的访问了临界资源,破坏了原子操作,造成了数据的不一致
      • 多线程访问的延迟和线程的随机性产生了线程的安全问题
      • 当某一线程对象进入run方法后,如果能做一个标记,说我已经在理面了,其他哥们(线程对象)你就等着吧,等我操作完了,出来了去掉标记你再进去吧,这样一来。原子操作就不会遭到破坏。
      • 具体体现就是给那个原子操作枷锁,是整个操作同步,不让其他线程对象破坏,保证数据的一致性。
    同步解决线程安全问题
    • 同步代码块

      • 同步代码块中的锁可以是任意对象,但是要在成员范围内定义
      • 在局部的话,会导致锁发生变化,因为你每次执行方法,都会重新创建一个对象
      • 同步的前提
        • 至少要有两个线程
        • 同一个锁
      • 同步的好处:提高了安全性
      • 同步的弊端:效率较低
    • 同步函数

      • 同步函数的使用

        • 用synchronized关键字修饰方法即可

          public synchronized void show(){
              //需要的同步代码块
          }
          
      • 同步函数使用的锁

        • 同步函数使用时this对象锁,静态同步函数的锁是(类名.class)
        public class Singleton{
            private Singleton(){}
            private static Singleton s = null;
            public static Singleton getInstance(){
                if(s==null){
                    synchronized(Singleton.class){
                        if(s==null){
                            s= new Singleton();
                        }
                    }
                }
             return s ;
            }
        }
        
    死锁

    每个线程都不会释放自己拥有的锁标记,却阻塞在另外的线程所拥有的锁标记的对象池中,就会造成死锁现象。

    产生原因

    假如有A何B两个锁,在A锁中要使用B锁,在B锁中要使用A锁,而他们都不想让,最终导致了死锁。

    • 因为资源不足
    • 进程运行推进的顺序不合适
    • 资源分配不当
    产生死锁的四个条件
    • 互斥条件:一个资源每次只能被一个进程使用

    • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

    • 不剥夺条件:进程已获得的资源,在未使用之前,不能强行剥夺。

    • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

      只要上述条件之一不满足,就不会发生死锁。

    如何解决?
    • 不同时在A锁中使用B锁,B锁中使用A锁
    线程状态
    • CPU

      • CPU的执行资格:可以被CPU的处理,在处理队列中排队
      • CPU的执行权:正在被CPU的处理
    • 创建:使用start()开启线程

    • 运行:具备着执行资格,具备着执行权。

    • 冻结:释放执行权,同时释放执行资格

      从运行到冻结的方式

      • sleep(time),sleep(time)时间到,进入临时阻塞状态(具备着执行资格,但是不具备执行权,正在等待执行权)
      • wait()线程等待,notify()线程唤醒,进入临时阻塞状态
    • 消亡

      从运行到消亡的方式

      • stop()终止线程
      • run()方法结束,线程任务结束
    几个方法的使用
    • 为什么wait(),notify(),notifyAll()都定义在Object类中?

      • 这些方法存在于同步中
      • 使用这些方法时必须要标识所属的同步锁
      • 锁可以是任意对象,所以任意兑现调用的方法一定定义Object类中
    • wait()和sleep()的区别?

      • 对时间指定货而言
        • wait():可以不指定时间
        • sleep():必须指定时间
      • 对执行权和锁而言
        • wait():释放了CPU的执行权(资格也没了),释放锁,存储于线程池。
        • sleep():释放CPU执行权,不释放锁(会自动醒)
    • 停止线程

      • 通过控制循环
      • interrupt()方法
        • stop已过时,被interrupt取代
    • 守护线程,后台线程

      你只要把一个线程设置为守护线程,那么主方法线程结束,不管什么情况,守护线程就结束。

    • join:加入线程,把执行权抢夺,自己执行完毕,其他线程才可能有机会执行

    • toString():线程名称,优先级,线程组(是有多个线程组成的,默认的线程组是main)

    • yield():让本线程暂停执行,把执行权给其他线程

    • setProproty(int num):设置线程的优先级

    • getPrinrity()获取线程的优先级

      • 线程级别1-10
      • 默认级别5
    LOCK&Condition接口
    Scanner类

    接收从键盘输入的数据

    • 这是java中提供的类。在util包中
    • 基本用法:
      • Scanner sc = new Scanner(System.in);
      • int a =sc.nextInt();


作者:关小涛
学习和分享是博客最大的乐趣,欢迎大家取之所需。
努力是自己努力的原因,每周天写博客总结工作中的新技能和出现的问题
原文地址:https://www.cnblogs.com/XtsLife/p/11015275.html