多线程学习笔记

1、简介

  • 进程:

  • 进程是操作系统结构的基础,计算机中正在运行的程序实例;可以分配给处理器并由处理器执行的一个实体;由单一顺序的执行显示,一个当前状态和一组相关的系统资源所描述的活动单元。简单的说,当我们启动一个应用程序,就会有个进程。可以通过任务管理器查看到当前系统的进程有哪些。系统给进程分配了独立的内存空间。一个正在运行的程序;

  • 线程:

  • 线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。一个执行路径

  • 每一个程序都至少有一个线程,那就是程序本身。线程是程序中一个单一的顺序控制流程。 在单个程序中同时运行多个线程完成不同的工作,称为多线程。

  • 单线程:同一个时刻,只允许执行一个线程

  • 多线程:同一个时刻,可以执行多个线程

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

  • 并行:同一个时刻,多个任务同时执行,多核cpu可以实现并行(也可以并发);

2、线程创建

  • 方案一:

  • 方式一:直接创建线程对象继承Thread类,重写run方法

  • 方式二:使用匿名内部类创建Thread子类对象,重写run方法

//方式一:
		优点:
			可以创建多次线程对象
		缺点:
			不便书写,可扩展性差
			
//方式二:
		优点:
			书写方便
		缺点:
			只能创建一个对象
  • 方案二:将线程(Thread)对象 与 线程任务(Runnable)分开1

  • 方式一:实现Runnable接口,重写run方法,将线程任务放入线程对象。

  • 方式二:使用匿名内部类,创建线程任务对象

//方式一:
		优点:
			将线程任务与线程对象分开
			任务对象可以创建多个
		缺点:
			创建复杂
			
//方式二:
		优点:
			书写方便
		缺点:
			代码混乱
			只能创建一个对象

3、线程的使用

package thread;

//继承Thread类实现线程
public class ClassThread01 extends Thread {

    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("run线程体-----" + i);
        }
    }

    public static void main(String[] args) {
        //main方法,主线程

        Runtime runtime = Runtime.getRuntime();
        int i1 = runtime.availableProcessors();//获取本机电脑的cpu数量
        System.out.println(i1);

        ClassThread01 classThread01 = new ClassThread01();

        classThread01.start();//启动线程

        for (int i = 0; i < 20; i++) {
            System.out.println("main线程体-----" + i);
        }
    }

}

4、线程方法

1、//设置线程名称
	steName()
    
2、//启动线程
    start()
    
3、//获取线程名称
    getName()
    
4、//获取当前线程对象
    currentThread()
    
5、//使当前线程休眠
    sellp(时间)
    
6、//线程的优先级
	线程的优先级取值范围为 1 ~ 10;
    setPriority()
7、//获取线程的优先级
    getPrioity()
    
8、//在指定的毫秒内让当前正在执行的线程休眠(即暂停执行),不会释放锁
    sellp()
 
9、//中断线程休眠
    interrupt()
    
10、//线程礼让。让出cpu,让其他线程执行,但礼让时间不确定,所以不一定礼让成功
    yield()
    
11、//线程插队。插队的线程一旦插队成功,则肯定会先执行完插入的线程所有的任务    
    join()
    
12、//设置线程为守护线程
    setDaemon( boolean b);

13、//线程的销毁,不建议使用
    stop()   与  destroy() 不要用
    当线程执行完run方法,就会等待被系统回收
  1. 线程的生命周期

  • 新建 (New)

  • 新建一个线程对象

  • 就绪 (Runnable)

  • 线程对象创建后,其他线程调用了该对象的start方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

  • 运行 (Running)

  • 就绪状态的线程获取了CPU,执行程序代码。

  • 阻塞 (Blocked)

  • 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态

  • 死亡 (Dead)

  • 线程执行完了或者因异常退出了run方法,该线程结束生命周期。

6、线程安全问题

  • 多个线程同时操作同一个数据源

//方案一: 同步代码块
语法:
	synchronized(锁对象){
			
	
	}
	//注意:保证多个线程的锁对象是同一个对象
	

//方案二: 同步方法
语法:
	public synchronized void method() {
			
	}
	//注意:同步方法的锁对象是调用该方法的对象
	

	
//方案三: 同步静态方法
语法:
	public static synchronized void method() {
			
	}
	/*
		类对象:
			当类被加载时,系统生成的一个对象,一个类只有一个类对象
         获取类对象
         	类名.class
         	对象名.getClass()
	*/

7、线程间通讯

  • 多个线程间消息的传递

//继承于Object的方法

1、//表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
	wait()
    
2、//指定等待的毫秒数
    wait(long timeout)
   2、//指定等待的毫秒数,与 指定的纳秒数
    wait(long timeout,int x ) 
    
3、//唤醒一个处于等待状态的线程 
    notify()
  
4、//唤醒同一个对象上所有调用wait()方法的线程,优先级别搞的线程优先调度
    notiflAll()

生产者与消费者模式

//工厂类
public class Factory {
    private int num;
    private final static int MAX = 100;

    //生产方法
    public synchronized void producerMethod() {
        String name = Thread.currentThread().getName();

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

        if (num <= MAX) {
            System.out.println(name + "正在生产商品,当前商品总量:" + (++num));
            notify();
        } else {
            System.out.println("仓库存储已满等待------售卖");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    //消费方法
    public synchronized void consumerMethod() {

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

        String name = Thread.currentThread().getName();
        if (num > 0) {
            System.out.println(name + "正在售卖商品,当前商品余量:" + (--num));
            notify();
        } else {
            System.out.println("仓位储存不足等待------生产");
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


    }
}
//生产者
public class producer implements Runnable {
    private Factory factory;

    public producer(Factory factory) {
        this.factory = factory;
    }

    @Override
    public void run() {
        while (true) {
             factory.producerMethod();
        }
    }
}
//消费者
public class Consumer implements Runnable{
    private Factory factory;

    public Consumer(Factory factory) {
        this.factory = factory;
    }

    @Override
    public void run() {
        while (true) {
            factory.consumerMethod();
        }
    }
}
//测试类
public class Text {
    public static void main(String[] args) {
        Factory factory = new Factory();
        Consumer consumer = new Consumer(factory);
        producer producer = new producer(factory);

        Thread thread01 = new Thread(producer, "唐三");
        Thread thread02 = new Thread(producer, "萧炎");

        Thread thread03 = new Thread(consumer,"小舞");
        Thread thread04 = new Thread(consumer,"萧薰儿");

        thread01.start();
        thread02.start();
        thread03.start();
        thread04.start();
    }
}

8、线程池

原因:

  • 一个线程大约占1MB的内存,在项目中大量创建线程会导致内存溢出;

  • 线程在执行完run方法后,会等待系统回收,会导致内存浪费;

  • 频繁的创建于回收线程,会导致资源浪费;

作用:

  • 管理线程,如线程的创建、销毁、复用等

本质:

  • 容纳多个线程的容器

//Executor(接口)
    execute(),//执行线程任务
//ExecutorServicer(接口) 是Executor的子接口
    shutdown()//关闭线程池
    isShutdown()//判断线程池是否关闭
    submit(Runnable task)//给线程池提交任务
    submit(Callable<T> task)/
        /给线程池提交线程任务
//ScheduledExecutorService(调度线程池)
    public ScheduledFuture<?> scheduleAtFixedRate(
    			Runnable  command,//线程任务
                 long  initialDelay,//延迟时间
                 long  period,//间隔时间
				TimeUnit  unit//时间单位
						)
     注意:下一次任务开始时间 - 上一次任务开始时间 = 间隔时间当任务执行时间大于间隔时间,下一次任务将会在上一次任务结束后,立即执行   	
//子类:
ThreadPoolExecutor
//构造函数:
  public ThreadPoolExecutor(
    int corePoolSize,//核心线程数量
    int maximumPoolSize,//最大线程数量
    long keepAliveTime,//线程回收时间
    TimeUnit unit,//时间单位
    BlockingQueue<Runnable> workQueue,//存储线程任务的队列
    hreadFactory threadFactory,//线程工厂
    RejectedExecutionHandler handler)//算法
  • 线程池的使用步骤

  • 创建线程池

  • 提交任务

  • 关闭线程池,注意抢占线程池中的线程默认为守护线程

Executors工具类

1、//创建 固定线程池(指定创建的线程数量)
	static ExecutorService newFixedThreadPool(int nThreads)
    
2、//创建 单列线程池(1次创建1个线程数量)
	static ExecutorService newSingleThreadExecutor()
    
3、//创建 可变线程池(int的最大值就是线程的上限,理论无上限)
	static ExecutorService newCachedThreadPool()

4、//创建 抢占线程池(创建时指定线程数量可以传入线程的数量,不传入,则默认使用当前计算机中可用的cpu数量)
    抢占线程池   中的线程是  守护线程
    一个执行结束的线程会在其他正在执行的线程抢占执行的结果
	static ExecutorService newWorkStealingPool(int parallelism)   
    
5、//创建 单列调度线程池(一个线程的定时任务)
	newSingleThreadScheduledExecutor()
    
6、//创建 调度线程池(多个线程的定时任务)  
	newScheduledThreadPool(int corePoolSize):    
    

9、Callable

  • 问题:

  • Runnabel接口无法返回数据

  • 解决方案:

  • 使用Callable

  • 注意:Callable的使用依赖于线程池

  • Callable与Runnable的区别

  • callable提供call方法,有返回值

  • Runnable提供run方法,没有返回值

						//Futre
作用:获取线程任务对象的返回值
方法:get()
    注意:会阻塞当前线程,知道对应的线程任务执行完毕后,获取其返回值

10、Lock

  • 作用:替换synchronized

  • 体系:

  • //Lock(接口) 提供方法:lock()锁 与 unlock()开锁 //子类: ReentrantLock:重入锁 //ReentrantReadWriteLock:读写锁 提供的方法 Lock readLock();获取读锁 Lock writeLock();获取写锁 注意: 读-读 不互斥 读-写 互斥 写-写 互斥 经验: 读里面用读锁 写里面用写锁

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

慕清海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值