ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

多线程基础

2021-09-20 17:05:34  阅读:102  来源: 互联网

标签:同步 Thread 基础 System 线程 println 多线程 方法


多线程基础

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

进程:

  • 进程是指运行中的程序,比如使用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()来控制线程

标签:同步,Thread,基础,System,线程,println,多线程,方法
来源: https://blog.csdn.net/weixin_42528988/article/details/120392983

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有