多线程基础

多线程基础

程序:为了完成任务用某种语言编写的一组指令的集合

进程:

  • 进程是指运行中的程序,比如使用QQ就是启动一个进程,操作系统就会为该进程分配内存空间。
  • 进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它本身的产生、存在和消亡的过程

线程:

  • 线程由进程创建,是进程的一个实体
  • 一个进程可以拥有多个线程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xdXejj5S-1632128244791)(C:\Users\91966\AppData\Roaming\Typora\typora-user-images\1629934401218.png)]

main线程运行60次,Thread-0子线程运行80次,当main线程结束后进程并没有结束,而是等所有线程结束后进程才会结束

start()方法调用**start0()**方法后,该线程不会立即执行,只是将线程变为可运行状态,具体什么时候执行,取决于CPU,由CPU统一调度

创建线程:

  1. 通过继承Thread类创建线程
    
  2. 通过实现接口Runnable来开发线程
    //这里无法再直接调用start()来启动线程
    //需要创建一个Thread对象,把dog对象(已实现Runnable接口)放入Thread
    Thread thread = new Thread(dog);
    thread.start();
    

继承Thread和实现Runnable接口的区别

  • 从java的设计上来看,通过继承Thread和实现Runnable接口本质上没有区别,从jdk文档上看Thread类本身就是实现Runnable接口
  • 实现Runnable接口更加适合多个线程共享一个资源的情况,并且避免了单继承。建议都是用Runnable

线程终止:

  • 线程完成后,自动退出
  • 还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式
线程常用方法1
  1. setName 设置线程名称,使之与参数name相同

  2. getName 返回该线程的名称

  3. start 使该线程开始执行;实际上是java虚拟机底层调用该线程的start0方法

    1. start底层会创建新的线程,调用run方法,run方法本身是一个简单地方法调用,不会启动新的线程
  4. run 调用线程对象run方法

  5. setPriority 更改线程的优先级

    1. 优先级范围:常用最小(MIN_PRIORITY = 1)是1;默认(NORM_PRIORITY = 5)是5;最大(MAX_PRIORITY = 10)是10
  6. getPriority 获取线程的优先级

  7. sleep 在指定的毫秒内让当前正在执行的线程休眠(暂停执行)

    1. 线程的静态方法,是当前线程休眠
  8. interrupt 中断线程

    1. 中断线程,但没有真正的结束线程。所以一般用来中断正在休眠的线程

      package com.lic.method;
      
      public class ThreadMethod01 {
          public static void main(String[] args) throws InterruptedException {
              T t = new T();
              t.setName("李琛");//给线程设置名称
              t.setPriority(Thread.MIN_PRIORITY);//设置优先级为最低
              t.start();
              for (int i = 0; i <5 ; i++) {
                  Thread.sleep(1000);
                  System.out.println("hi"+i);
              }
              System.out.println(t.getName()+"线程的优先级为"+t.getPriority());
              t.interrupt();//执行到这里就会中断t线程的休眠
          }
      }
      class T extends Thread{
          @Override
          public void run() {
              while (true){
              for (int i = 0; i <100 ; i++) {
                  System.out.println(Thread.currentThread().getName()+"吃包子"+i);//获取线程名称
              }
              try {
                  System.out.println(Thread.currentThread().getName()+"休眠中");
                  Thread.sleep(20000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
                  //当线程执行到一个interrupt方法时,就会catch一个异常,可以加入自己的业务代码
                  //InterruptedException是捕获到的一个中断异常
                  System.out.println(Thread.currentThread().getName()+"被interrupt了");
              }
              }
          }
      }
      
线程常用方法2
  1. yield 线程礼让。让出cpu,让其他线程先执行,但礼让的时间不确定,所以也不一定礼让成功

  2. join 线程的插队。插队的线程一旦插队成功,则肯定先执行完插入线程所有的任务

    package com.lic.method;
    
    public class ThreadMethod02 {
        public static void main(String[] args) {
            M1 m1 = new M1();
            m1.setName("子线程");
            m1.start();
            for (int i = 1; i <=20 ; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("hi "+Thread.currentThread().getName()+i);
                if (i==5){
                    //try {
                        System.out.println("先运行完毕子线程");
                        Thread.yield();//礼让子线程,但是不一定成功
                   //     m1.join();//线程插队
                    //} catch (InterruptedException e) {
                    //    e.printStackTrace();
                    //}
                    System.out.println("子线程运行完毕,主线程继续运行");
                }
            }
        }
    }
    class M1 extends Thread{
        @Override
        public void run() {
            for (int i = 1; i <=20 ; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("hello "+Thread.currentThread().getName()+i);
            }
        }
    }
    
现场常用方法3
  1. 用户线程 也叫工作线程,当线程的任务执行完成或通知的方式结束

  2. 守护线程 一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束

  3. 常见的守护线程:垃圾回收机制

    package com.lic.method;
    
    public class ThreadMethod03 {
        public static void main(String[] args) throws InterruptedException {
            MyDaemonThread myDaemonThread = new MyDaemonThread();
            //如果需要在主线程结束后,子线程自动结束。只需要将子线程设置成守护线程
            myDaemonThread.setDaemon(true);//需要先设置成守护线程再启动
            myDaemonThread.start();//DaemonThread:守护线程
            for (int i = 1; i <=10 ; i++) {
                System.out.println("宝强在辛苦的工作");
                Thread.sleep(1000);
            }
            System.out.println("宝强不工作回家了,马蓉和宋喆也不能聊天了");
        }
    }
    class MyDaemonThread extends Thread{
        @Override
        public void run() {
            while(true){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("马蓉和宋喆快乐聊天");
            }
        }
    }
    
    

线程的生命周期 **

线程同步机制

  1. 在多线程中一些敏感的数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时候最多只有一个线程访问,以保证数据的完整性

  2. 也可以这样理解:线程同步,即当有一个线程对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作

    • 具体方法

        1. 同步代码块

          synchronized(对象){ //得到对象的锁才能操作同步代码
              //需要被同步的代码
          }
          
        1. synchronized还可以放在方法声明中,表示整个方法为同步方法
        public synchronized void m (String name){
               //需要被同步的代码
        }
        

    互斥锁

    1. 局限性:导致程序的执行效率降低

    2. 非静态的同步方法的锁可以是this,也可以是其他对象(要求是同一个对象)

    3. 静态的同步方法的锁为当前类本身

          //静态的同步方法的锁为当前类本身
          public synchronized static void m1(){} //锁是加在SellTickets3.class上
          //如果要在静态方法中,实现一个静态代码块
          public static void m2(){
              synchronized (SellTickets3.class){//SellTickets3.clas:类本身
              System.out.println("m2");
              }
          }
      

      互斥锁注意事项:

      1. 同步方法如果没有使用static修饰,默认锁的对象是this
      2. 如果方法使用static修饰,默认锁的对象是 类.class
      3. 实现步骤:
        • 需要分析上锁的代码
        • 选择同步代码或同步方法
        • 要求多个线程的锁对象为同一个即可

    线程的死锁

    多个线程都占用了对方的资源,但不肯相让,导致了死锁,在编程一定要避免死锁的发生

    下面操作会释放锁:

    1. 当前线程的同步方法、同步代码块执行结束
    2. 当前线程在同步方法、同步代码块中遇到break和return
    3. 当前线程在同步方法、同步代码块中遇到Error和Exception,导致异常结束
    4. 当前线程在同步方法、同步代码块中执行了线程对象的wait()方法,当前线程暂停,并且释放锁

    下面的操作不会释放锁:

    1. 线程执行同步方法、同步代码块时,程序调用Thread.sleep()和Thread.yield()方法时暂停当前线程的执行,不会释放锁
    2. 线程执行同步代码块时,其他线程调用了suspend()方法将此线程挂起,该线程不会释放锁
      • 避免使用suspend()来控制线程

、同步代码块中遇到Error和Exception,导致异常结束
4. 当前线程在同步方法、同步代码块中执行了线程对象的wait()方法,当前线程暂停,并且释放锁

下面的操作不会释放锁:

  1. 线程执行同步方法、同步代码块时,程序调用Thread.sleep()和Thread.yield()方法时暂停当前线程的执行,不会释放锁
  2. 线程执行同步代码块时,其他线程调用了suspend()方法将此线程挂起,该线程不会释放锁
    • 避免使用suspend()来控制线程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值