Java多线程 入门

前言

第一次系统的接触多线程(B站老韩的课),写了点笔记用来反思总结




基本概念

进程

  1. 运行中的程序,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。类比打开下载器,开启一个进程
  2. 进程是程序的一次执行过程,是一个正在执行的程序,是动态过程,有它自身的产生、存在、消亡的过程

线程

  1. 线程是由进程创建的,是进程额度一个实体
  2. 一个进程可以拥有多个线程,类比一个下载器可以同时下载多个文件

相关概念

  1. 单线程:同一时刻只允许执行一个线程
  2. 多线程:同一时刻可以执行多个线程
  3. 并发:同一时刻多个任务交替进行,形成“同时进行”的效果,单核cpu实现的多任务就是并发
  4. 并行:同一时刻,多个任务同时执行,多核cpu可以实现并行

线程基本使用

创建线程三种方式

  1. 继承Thread类,重写run方法
  2. 实现Runnable接口,重写run方法
  3. 实现Callable接口,重写call方法(暂且不提)

示例代码

继承Thread
package com.suen.demo;

/**
 * @author Suen
 * @date 2021/7/28 0:09
 */
public class Thread01 {
    public static void main(String[] args) {
        Runner runner = new Runner();
        /**
         * 解读源码
         * 1. public synchronized void start() {
         *          start0();
         *    }
         *
         * 本地方发 由JVM调用,底层是 c / c++
         * 2. private native void start0();
         *
         * 注意:
         * start方法偶用start0方法之后,该线程不一定会立刻执行,
         * 只是将该线程变成了可运行状态,具体什么时候执行取决与CPU的统一调度
         */
        
        /**
         * runner.start(); //  启动线程->最终会执行Runner的run方法
         * runner.run();   //  run方法就是一个普通的方法,没有真正的启动一个线程,就会把run方法执行完毕,才向下执行(串行)
         * 说明:当 main 线程启动一个子线程 Thread-0,主线程不会阻塞,会继续进行
         * 这时,主线程和子线程是交替执行
         */
        int times = 0;
        while (++times <= 20) {
            System.out.println("线程名=" + Thread.currentThread().getName());
            try { Thread.sleep(1000); }
            catch (InterruptedException e) { e.printStackTrace(); }
        }
    }
}

class Runner extends Thread {
    int times = 0;

    @Override
    public void run() {
        while (times <= 30) {
            System.out.println("run code~" + (++times) + " 线程名=" + Thread.currentThread().getName());
            try { Thread.sleep(1000); }
            catch (InterruptedException e) { e.printStackTrace(); }
        }
    }
}

实现Runnable
package com.suen.demo;

/**
 * @author Suen
 * @date 2021/7/28 16:42
 * 通过实现接口Runnable 来开发线程
 * 设计模式【代理模式】
 */
public class Thread02 {
    public static void main(String[] args) {
        Runner02 runner02 = new Runner02();
        //  runner02.start(); 这里不能调用start
        //  创建了Thread对象,Runner02(实现Runnable)放入Thread
        Thread thread = new Thread(runner02);
        thread.start();
    }
}

class Runner02 implements Runnable {

    int count = 0;

    @Override
    public void run() {
        while (true) {
            System.out.println("run code~" + (++count) + Thread.currentThread().getName());

            try { Thread.sleep(1000); }
            catch (InterruptedException e) { e.printStackTrace(); }
        }
    }
}

区别 继承Thread 实现Runnable接口

  1. 两种方式创建线程的本质没有区别,Thread本身就是实现了Runnable接口
  2. 实现Runnable接口的方法更加适合多个线程共享一个资源的情况,并且避免了多线程的限制

终止线程

  1. 当线程完成任务后会自动退出
  2. 使用变量控制run方法停止线程,也叫通知方式

Thraed类常用方法

1. setName		//	设置县城名称,是指与参数name相同
2. getName		//	返回该线程的名称
3. start		//	使该线程开始执行;Java虚拟机 底层调用该线程恩都 start0() 方法
4. run			//	调用线程对象的 run 方法
/** start底层会创建新的线程,调用run 而run就是一个简单的方法调用,不会启动线程 */
5. setPriority	//	更改线程的优先级
6. getPriority	//	获取线程的优先级
/** 范围在 1 ~ 10 */
7. sleep		//	在指定的毫秒数内让当前正在执行的线程休眠
/** 线程的静态方法,是当前线程休眠 */
8. interrupt	//	中断线程(区别于终止)
/** 一般用于终端正在休眠的线程 */
9. yield		//	让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
10.join			//	线程的插队。插队的线程一旦插队成功,则肯定限制性插入的线程的所有任务

代码

package com.suen.method;

/**
 * @author Suen
 * @date 2021/7/28 18:46
 */
public class ThreadMethod01 {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.setName("suen");
        t.setPriority(Thread.MIN_PRIORITY);
        t.start();

        for (int i = 0; i < 5; i++) {
            Thread.sleep(1000);
            System.out.println("hi " + i);
        }

        t.interrupt();
    }
}

class T extends Thread {
    @Override
    public void run() {
        while (true) {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + " hhh" + i);
            }

            try {
                System.out.println(Thread.currentThread().getName() + " 休眠中");
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + " 被interrupt了");
            }
        }
    }
}

线程的 7 种状态

  • NEW
    尚未启动的线程处于此状态。
  • RUNNABLE(Ready + Running)
    在Java虚拟机中执行的线程处于此状态。
  • BLOCKED
    被阻塞等待监视器锁定的线程处于此状态。
  • WAITING
    正在等待另一个线程执行特定动作的线程处于此状态。
  • TIMED_WAITING
    正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
  • TERMINATED
    已退出的线程处于此状态。 、

Synchronized关键字

线程同步机制

  1. 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多只有一个线程访问,以保证数据的完整性
  2. 当有一个线程对内存进行操作时,其他线程都不可以对这个内存地址进行操作,知道该线程完成操作,其他线程才能对该内存地址进行操作。

具体同步方法

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

Java中常见的锁

下面这篇文章写的很好,可以看看。

图解Java中的那18种锁


互斥锁

  1. Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
  2. 每个对象都对应于一个可称为”互斥锁“的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
  3. 关键字synchronized来与对象的互斥锁联系,当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问
  4. 同步的局限性:程序执行效率下降
  5. 非静态同步方法的锁可以是this,也可以是其他对象(要求是同一个对象
  6. 静态同步方法的锁为当前类本身

死锁

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

举例方便理解
老板:你有经验,我再用你
员工:我得被用,才有经验


释放锁

线程正常释放锁的情况

  • 当前现成的同步方法、同步代码块执行结束
  • 当前线程在同步代码块、同步方法中遇到break、return
  • 当前线程带同步代码块、同步方法种出现了未处理的Error或Exception,导致异常结束
  • 当前线程在同步代码块、同步方法中执行了线程对象的wait方法,当前线程暂停,并释放锁

线程不释放锁的情况

  • 线程执行同步代码块或者同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁
  • 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程给挂起,该线程不会释放锁

总结

以上只是多线程的入门知识,多线程可以说是目前互联网行业实际开发中最复杂、难度最高的问题,以后会做进一步补充。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值