Bootstrap

多线程(基础)学习韩顺平老师的B站视频

多线程(基础)

线程介绍

  • 进程

进程是指运行中的程序,启动一个进程,操作系统就会为该进程分配内存空间。

进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:有它自身的产生、存在和消亡的过程。

  • 线程

线程由进程创建,是进程的一个实体。

一个进程可以有多个线程。

  • 并发与并行
    1. 并发:同一时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的说,单核cpu实现的多任务就是并发
    2. 并行:同一时刻,多个任务同时执行,多核cpu可以事项并行。

线程使用

  • 创建线程的两种方法

    1. 继承Thread类,重写run方法。
    2. 实现Runnable接口,重写run方法。(主要是因为java一个类只能继承一个父类,但是可以继承多个接口)
    3. 实现callable接口,重写run方法。(具有返回值)
  • 为什么启动多线程调用的是start方法,不是run方法

    继承后实现的run方法,如果调用的是它,那就相当没有开启多线程,此时的run方法相当一个普通方法。

    具体可以去看start方法源码;

    (1)
    public synchronized void start(){
       start0();
    }
    (2)
    //srarr0是本地方法,是JVM调用,底层是C/C++实现
    //真正实现多线程的效果,是start0(),而不是run
    private native void start0();
    
  • 关于实现runnable接口的类需要实例Thread对象,传入继承runnable的类对象,再调用start方法开启线程,这种方式使用了设计模式—静态代理(因为runnable中只有run方法,没有start方法)。

    class Dog implements Runnable{
        @Override
    	public void run() {
    		// TODO Auto-generated method stub
    		
    	}
    }
    public static void main(String[] args) {
    		Dog dog=new Dog();
           Thread thread=new Thread(dog);
            thread.start();
    }
    
    
  • 实现Runnable接口方式更加适合多个线程共享一个资源的情况,且避免了单继承的限制(若用继承Thread类多个线程共享一个资源则需要将共享的资源定义为static)。

    //假设T3继承Runnable接口,并实现了run方法
    T3 t3=new T3("hello");
    Thread thread01=new Thread(t3);
    Thread thread02=new Thread(t3);
    thread01.start();
    thread02.start();
    

线程方法

  • 线程终止

    • 基本说明
      1. 当线程完成任务后,会自动退出。
      2. 还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式。
    • 例:需求是启动一个线程 t,要求再main线程中去停止线程t,请编程实现。
    • 实现:设置一个boolean类型的变量去控制while循环。
  • 常用方法

    • setName 设置线程名称,使之与参数name相同
    • getName 返回该线程的名称
    • start 是该线程开始执行
    • run 调用线程对象run方法
    • setPriority 更改线程的优先级
    • getPriority 获取线程的优先级
    • sleep 在指定的毫秒数内让当前正在执行的线程休眠
    • interrupt 中断线程,并没有真正的结束线程,所以一般用于中断正在休眠的线程
  • 线程插队

    • yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功

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

      • 案例:创建一个子线程,每隔1s输出hello,输出20次,主线程每隔1s,输出hi,输出20次,要求:两个线程同时执行,当线程输出5次后,就让子线程执行完毕,主线程再继续。

        public class demo2 {
        	public static void main(String[] args) throws Exception {
        		Thread thread = new Thread(new T());
        		thread.start();
        		for (int i = 0; i < 20; i++) {
        			Thread.sleep(1000);
        			System.out.println("主线程吃了" + i + "包子了");
        			if (i == 5) {
        				System.out.println("主线程先让子线程吃完");
        				thread.join();
        			}
        		}
        	}
        }
        
        class T implements Runnable {
        
        	@Override
        	public void run() {
        		// TODO Auto-generated method stub
        
        		for (int i = 0; i < 20; i++) {
        			try {
        				Thread.sleep(1000);
        			} catch (InterruptedException e) {
        				// TODO Auto-generated catch block
        				e.printStackTrace();
        			}
        			System.out.println("子线程吃了" + i + "包子了");
        		}
        	}
        
        }
        
        
  • 守护线程

    • 用户线程和守护线程
      1. 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
      2. 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束。
      3. 常用的守护线程:垃圾回收机制

线程生命周期

  • 线程状态。

    线程可以处于以下状态之一:

    • NEW
      尚未启动的线程处于此状态。

    • RUNNABLE
      在Java虚拟机中执行的线程处于此状态。

    • BLOCKED

      被阻塞等待监视器锁定的线程处于此状态。

    • WAITING
      正在等待另一个线程执行特定动作的线程处于此状态。

    • TIMED_WAITING

      正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。

    • TERMINATED

      已退出的线程处于此状态。

    一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。

Synchronized

  • 线程同步机制

    1. 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性。
    2. 也可以这样理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作
  • 同步具体方法-Synchronized

    1. 同步代码块

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

      public synchronized void m(String name){
          //需要被同步的代码
      }
      
    3. 如何理解:就好像某位小伙伴上厕所前先把门关上(上锁),完事后再出来(解锁),那么其它小伙伴就可再使用厕所了。

    4. 使用synchronized解决售票问题。

互斥锁

  • 基本介绍
    1. Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
    2. 每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证再任一时刻,只能有一个线程访问对象。
    3. 关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象再任一时刻只能由一个线程访问。
    4. 同步的局限性:导致程序的执行效率要降低
    5. 同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是通一个对象)
    6. 同步方法(静态的)的锁为当前类本身。
  • 注意事项和细节
    1. 同步方法如果没有使用static修饰:默认锁对象为this
    2. 如果方法使用static修饰,默认锁对象:当前类.class
    3. 实现的落地步骤:
      • 需要先分析上锁的代码
      • 选择同步代码块或同步方法
      • 要求多个线程的锁对象为同一个即可

死锁

  • 基本介绍

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

;