多线程基础学习笔记

课程连接:B站狂神说

基本概念

进程

执行程序的一次执行过程。是系统资源分配的单位。

线程

线程是CPU调度和执行的基本单位。一个进程至少包含一个线程。即使没有再创建线程,程序执行也是多线程的,main,GC线程

线程的创建(推荐使用第二个,因为类的单继承局限性)

创建一个类 继承Thread,并重写run方法,调用时使用start开启线程

开启的时候使用的是start方法,如果使用run,依旧是单线程。线程开启之后不一定直接执行,而是根据CPU的调度。

创建一个类 实现Runnable接口,重写run方法,调用时先创建类,然后使用Thread.start开启线程

class c=new class;
new Thread(c).start();
//使用接口实现,可以避免单继承局限性,方便一个对象被多个线程使用
package thread;

import java.util.TreeMap;

public class demo01 implements Runnable {
    private static int number=10;

    @Override
    public void run() {
        while(number>0){
            try {
                System.out.println(Thread.currentThread().getName()+"拿到了第"+number+"票");
                number--;
                Thread.sleep(0);
            }catch (Exception e){
                System.out.println(e);
            }
        }
    }

    public static void main(String[] args) {
    	//出现并发问题
        demo01 d=new demo01();
        new Thread(d,"YH").start();
        new Thread(d,"DC").start();
        new Thread(d,"UI").start();
    }
}

创建一个类,实现Callable方法,需要返回值,重写call方法,并且需要开启服务,关闭服务,抛出异常

package thread;
import java.util.concurrent.*;
public class demo02 implements Callable<Boolean> {
    private static int number=10;
    @Override
    public Boolean call() throws Exception {
        while(number>0){
            try {
                System.out.println(Thread.currentThread().getName()+"拿到了第"+number+"票");
                //currentThread()获得当前线程
                number--;
                Thread.sleep(0);
            }catch (Exception e){
                System.out.println(e);
            }
        }
        return true;
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        demo02 d1=new demo02();
        demo02 d2=new demo02();
        demo02 d3=new demo02();
        demo02 d4=new demo02();
        //创建服务
        ExecutorService es= Executors.newFixedThreadPool(3);
        //提交线程
        Future<Boolean> submit1 = es.submit(d1);
        Future<Boolean> submit2 = es.submit(d2);
        Future<Boolean> submit3 = es.submit(d3);
        Future<Boolean> submit4 = es.submit(d4);
        //获得结果
        Boolean res1 = submit1.get();
        Boolean res2= submit2.get();
        Boolean res3 = submit3.get();
        Boolean res4 = submit4.get();
        //关闭服务
        es.shutdown();
    }
}

线程底层的原理 静态代理

原理

  • 真实对象和代理对象需要实现同一个接口
  • 真实对象要作为参数传入到代理对象中(相当于类中再传入一个类,但是两个都实现同一个接口,外层可以对接口的方法重写进行增强)
  • 优点:两个接口分开,真实对象可以实现其他功能,代理对象增强接口的功能

对比动态代理(AOP面向切面编程使用到)

  • 静态代理在编译期,动态代理在运行期
  • 静态代理手写,动态代理右JDK提供的工具类,需要按照格式进行写
  • 静态代理只能代理实现接口的类,动态代理又分为基于接口的和基于子类的
  • 静态代理一个代理对象只能代理一种被代理对象,动态代理可以代理多个,是基于反射实现的 看注解与反射的笔记

线程使用

线程的五个状态

在这里插入图片描述
在这里插入图片描述

线程的操作方法

在这里插入图片描述

线程的停止

  • 建议线程正常停止:利用次数,不建议死循环
  • 建议使用标志位停止:定义一个公共的方法,在主方法中进行控制,来结束线程
  • 尽量不要使用stop和destory方法

线程的休眠

  • sleep 指定当前线程阻塞的毫秒值 在时间到了之后,进入就绪状态。
  • 需要抛出InterruptedException异常。
  • sleep不会释放锁。
  • 模拟倒计时:利用系统时间进行显示时间
  • 模拟网络延时:方法问题的发生

线程的礼让

  • yield方法,让当前线程暂停,进入就绪状态,然后交由CPU重新调度
  • 礼让是从运行状态转到就绪状态,不会进入阻塞状态
  • 礼让不一定成功,CPU重新调度,可能还是会执行礼让的线程

线程的合并(插队)

  • join方法 直接执行合并的线程(插队),强制执行,其他线程阻塞,等该线程结束之后,才会继续执行其他线程(和方法的调用差不多)

线程的观测

Thread thread=new Thread(()->{
		//方法体
	})
Thread.State state=new thread().getState();//创建
state=thread.getState();//更新

线程的优先级

  • thread.setPriority() 设置优先级
  • thread.getPriority() 查询优先级 currentThread() 获得当前线程对象
  • 一共10个优先级,优先级大不一定先执行 只能是被调度的可能大
  • 主线程的优先级为5
  • 需要设置时,先设置再start

线程的分类

  • 守护线程:thread.setDeamon(true) 设置为守护线程 虚拟机不用等待守护线程结束
  • 用户线程:默认为用户线程 虚拟机必须等待用户线程结束

线程的同步---->队列加锁解决安全性

并发

多个线程访问同一资源

同步

锁机制 synchronized

使用锁可能引发的问题
使用synchronized关键字实现同步机制
只读的代码不用加synchronized,需要修改的才需要,如果synchronized太多,就会降低效率

  • 同步方法 在方法上,返回值之前加synchronized关键字 其实锁的就是调用方法的this
  • 同步代码块 把需要锁的对象放入参数,然后写代码 锁的对象就是需要增删改查的量
synchronized(number//该对象称为同步监视器){
while(number>0){
            try {
                System.out.println(Thread.currentThread().getName()+"拿到了第"+number+"票");
                //currentThread()获得当前线程
                number--;
                Thread.sleep(0);
            }catch (Exception e){
                System.out.println(e);
            }
        }
}

lock锁

private final ReentrantLock lock=new ReentrantLock();// ReentrantLock可重入锁

while(number>0){
            try {
            lock.lock();//开启锁
                System.out.println(Thread.currentThread().getName()+"拿到了第"+number+"票");
                //currentThread()获得当前线程
                number--;
                Thread.sleep(0);
            }catch (Exception e){
                System.out.println(e);
            }finally{
            	lock.unlock(); //打开锁
            }
        }

对比

在这里插入图片描述

死锁

某个同步代码块同时拥有两个以上对象的suo,就可能发生死锁。
在这里插入图片描述
JOC

线程通信

在这里插入图片描述

线程池

在这里插入图片描述

在这里插入图片描述

可能的面试问题

start和run的区别

  • run是在主线程中调用方法,实际上没有开启新的线程;
  • start是开启一条新的线程,然后调用其中的主方法。
  • 即run没有开启新的线程,start开启了新的线程,所以一般使用start方法,重写run方法

sleep和wait的区别

  • sleep是Thread中的方法,表示的是线程休眠一段时间,之后进入就绪状态;wait是Object的方法,被final修饰,表示线程进入等待状态,直到被唤醒(notify或者notifyAll)
  • sleep可以在任意地方使用,wait只能在同步方法或同步代码块中使用
  • sleep不会释放锁,wait是释放锁的
  • sleep需要捕获异常 InterruptedException wait不需要捕获异常

优先级倒置问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值