线程大总结

线程是进程的最小执行单元,具有抢占式运行、资源共享、并发和并行等特点。线程的生命周期包括启动、可运行、运行、阻塞和消亡阶段。创建线程有两种方式:继承Thread类或实现Runnable接口。线程同步包括同步方法和同步代码块,防止线程安全问题。Java提供了锁机制如synchronized和Lock来解决同步问题。线程池是一种优化线程使用的技术,可以控制并发线程数,减少资源消耗。
摘要由CSDN通过智能技术生成

什么是线程呢?

进程是由一个或者多个线程组成的,线程是进程最小的基本单位。

线程有什么特点?

        1.抢占式运行 :给程序分配cpu,按照时间片来执行,单位时间片抢占执行

        2.资源共享 :同一个进程,有多个线程,这个多线程是可以共享同一个数据

        3.并行:真正的同时执行

        4.并发:同时发生,轮流交替执行

线程的生命周期   

        1.线程的启动 start

        2.线程可运行状态 抢占 等待CPU

        3.线程运行状态 抢占和执行

        4.线程的阻塞 wait sleep锁

        5.线程消亡 destroy

创建线程的两种方式

        1.继承Thread类,重新run方法 实例化Thread子类,然后启动一个线程start()

class MyThread1 extends Thread{
    //1.一个是将一个类声明为`Thread`的子类
    @Override
    public void run() {
        //2.重写run方法
        for (int i = 1; i <=9 ; i++) {
            for (int j = 1; j <=i; j++) {
                System.out.print(i+"+"+j+"="+i*j+" ");
            }
            System.out.println();
        }
    }
}
class MyThread2 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <= 9; i++) {
            for (int j = 0; j <=i ; j++) {
                System.out.print("*");
            }
            System.out.println();
        }
    }
}
public class Demo1 {
    public static void main(String[] args) {
        MyThread1 myThread1 =new MyThread1();
        //3.新建线程的实例
        myThread1.start();
        //4.启动线程  启动
        MyThread2 myThread2 =new MyThread2();
        myThread2.start();
    }
}

        2.实现Runnable接口,重写run方法实例化Runnable接口的实现类。然后作为参数给Thread

public class Demo2 {
    public static void main(String[] args) {
        new Thread(new Runnable() {   //1.创建一个Thread线程。2.实现接口Runnable
            @Override
            public void run() {       //3.重写方法
                for (int i = 0; i < 1000; i++) {
                    System.out.println("第一个"+i);
                }
            }
        }).start();                 //4.重写方法
        new Thread(new Runnable() {     //1.创建一个Thread线程。2.实现接口Runnable
            @Override
            public void run() {         //3.重写方法
                for (int i = 0; i < 1000; i++) {
                    System.out.println("第二个"+i);
                }
            }
        }).start();                     //4.重写方法
    }
}

线程的方法

构造方法:

Thread()

Thread(Runnable target)

分配一个新的Thread对象写法一

Thread(Runnable target, String name)

分配一个新的Thread对象。并且对这个线程起一个名字

普通方法

 注意:优先级方法和sleep方法存在线程安全问题,不靠谱,真实开发中不会使用

线程同步和锁

当多个线程同时请求一个数据的时候,会导致数据不准确的情况。相互之间产生问题。

容易出现线程安全的问题。比如多个线程操作同一个数据,都打算修改商品的库存。

线程的同步真实的意思:让你"排队",几个线程之间要排队。一个一个对共享资源进行操作,等一个结束以后,另一个再进来操作。变量是唯一的和准确的

可以加锁

方法一:同步方法

同步方法:
	public synchronized void eat () {
	
}
只能有一个线程进入到方法中,其他线程在方法的外面等待

方法二:同步代码块

同步代码块:  将一段代码放到synchronized 然后括起来,就会对这段代码加上锁
语法格式:
synchronized (this) {
			
		}

java中的锁

synchronized 被成为隐式锁,会自动释放,是一个非公平的锁。

Lock锁 被成为显示锁,Lock需要主动解锁。

         Lock锁 只能有一个线程进入到方法中,其他线程在方法的外面等待
         使用时候可能会用到try catch finally用来保证解锁
         需要主动解锁

他们两个锁都可以解决线程同步的问题。但是synchronized 更加强大,更加粒度化。更加灵活。

守护线程

守护线程是用来守护非守护线程的

非守护线程死,守护线程陪葬

问题:main方法中的非守护线程死亡,为什么控制台还会运行守护线程

        主线程虽然结束了,守护线程不会立即结束

        因为jvm虚拟机里面有两个线程,一个主线程一个垃圾回收线程。

        垃圾回收线程没处理完,所以守护线程是会继续运行极短时间

死锁问题

死锁是一种状态,当两个线程互相持有对象所需要的资源的时候,这两个线程又不主动释放资源就会导致死锁。代码无法正常执行。这两个线程就会僵持住。

应用场景:并发场景,多个线程。线程之间在共享数据的时候 是互不想让的线程枷锁为了线程安全,但是物极必反

Object类下面的和线程相关的方法

wait和notify:

​wait()方法和notify()方法调用时,需要保证同一个类 导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法。 ​

class Message {
    private String message;//信息

    public Message(String message) {//有参构造 获取实例化的message的名字
        this.message = message;
        System.out.println(this.message);
    }

    public void setMessage(String message) {//有参无返方法 设置message对象的名字
        this.message = message;

    }

    public String getMessage() {    //无参无返 获取message对象的名字
        return message;
    }

}

class WaitThread implements Runnable {    //线程类  等待线程
    private Message message;
    //把Message类当做属性  为了使用wait notify 时是一个对象调用的

    public WaitThread(Message message) {  //有参构造 把Message的message值传进去
        this.message = message;
    }
//等待线程抢到了
    //等待线程睡了5秒  然后唤醒线程执行。  synchronized (message)
    //message  对象从等待池中国去取的,结果发现没有  阻塞
    //回到等待线程睡醒了以后开始   wait等待

    @Override
    public void run() {     //重写run方法
        String name = Thread.currentThread().getName();   //显示当前线程是哪个线程
        System.out.println(name + "正在等待中....");

        synchronized (message) {   //上锁防止别的对象继续进入
            try {
                //当调用wait方法的时候,会自动释放锁,并将对象放到等待池中,让唤醒线程锁来操作这个对象
                //
                message.wait();//代码走到这一步 当前线程会等待!!!
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name +"被唤醒!!!Line51行的");
            System.out.println(message.getMessage());
            //验证唤醒线程是否来过,来过会显示修改过的message
        }
    }
}
//唤醒线程
class NotifyThread implements Runnable {
    private Message message;            //把Message类定义成属性  为了使用wait notify 时是一个对象调用的

    public NotifyThread(Message message ) {   //有参构造 把Message实例化的对象传参进来
        this.message = message;
    }
    @Override public void run() {
        try {
            Thread.sleep(1000);             //休眠1秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("唤醒线程已经执行");
        synchronized (message) {               //上锁防止别的对象继续进入
            message.setMessage("我是修改之后的message对象");
            //修改message,如果输出了,说明唤醒线程去过了等待池
            //message.notify();
            message.notifyAll();
            //执行唤醒

        }
    }
}
public class Demo1 {
    public static void main(String[] args) {
        Message message = new Message("我是message对象");      //实例化Message
        WaitThread waitThread = new WaitThread(message);       //实例化普通类1
        WaitThread waitThread1 = new WaitThread(message);       //实例化普通类2
        NotifyThread notifyThread = new NotifyThread(message);  //实例化普通类3
        new Thread(waitThread1, "wait2线程").start();       //普通类1转换成等待线程1
        new Thread(waitThread, "wait1线程").start();         //普通类2转换成等待线程2
        new Thread(notifyThread, "notify线程").start();       //普通类3转换为唤醒线程1
    }
}

join方法

join的作用:让主线程等待、一直等到子线程结束后,主线程才会执行

底层是wait  notify

问题:让线程1执行然后再接着线程2,怎么办

        在主线1中实例化线程2并启动调用join()方法

线程的经典案例

卖家买车案例

汽车生产商(卖家)

收到买家的购车需求,如果有车直接购买,如果没有车,让买家等待(wait)等待生产出来之后,通知买家(唤醒notify)买家去提车

购车用户(买家)

向卖家提出购车需求,如果有车直接买,如果没车,进入等待状态(wait)

class Goods{    //商品类 可以看做生产者和消费者交流的通道
    private String name;  //商品名字
    private double price;  // 商品价格
    private boolean isProduct; //是否需要生产
    public Goods(String name, double price, boolean isProduct) {
        this.name = name;
        this.price = price;
        this.isProduct = isProduct;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    public boolean isProduct() {
        return isProduct;
    }
    public void setProduct(boolean product) {
        isProduct = product;
    }
}
class Cusyomer implements  Runnable{  //消费者线程
    Goods goods;

public Cusyomer(Goods goods) {
    this.goods = goods;
}
@Override
public void run() {
    while (true){   //ture 让消费者一直消费
        synchronized (goods){   //锁定商品
            if (!goods.isProduct()){   //判断商品是否有库存,是否需要生产 true无库存需要生产  fales有库存不需要生产
                System.out.println("消费者买到了一辆"+goods.getName()+"价格为:"+goods.getPrice());
                //买车
                goods.setProduct(true);
                //修改库存
            }else {  //无库存状态
                try {
                    goods.wait();  //没库存需要等
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

}
class Prodector implements Runnable{  //生产者线程
    Goods goods;

public Prodector(Goods goods) { //有参构造 获取商品的当前的属性
    this.goods = goods;
}
@Override
public void run() {
    int count =0;    //计数器,为下面的生产何种车做准备
    while (true){   //让生产者一直生产
        try {
            Thread.sleep(2000);  //让生产者睡觉,保证消费者线程先走
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (goods){    //锁定商品
            if (goods.isProduct()) {   //判断库存状态 true需要生产 fales不需要生产
                if (count%2==0){     //偶数生产呜呜车
                    goods.setName("呜呜车");   //修改商品属性-名字
                    goods.setPrice(998);     //修改商品属性-价格
                }else {
                    goods.setName("滴滴车");   //修改商品属性-名字
                    goods.setPrice(688);     //修改商品属性-价格
                }
                goods.setProduct(false);    //生产好修改商品库存
                System.out.println("汽车商生产了:"+goods.getName()+"价格为"+goods.getPrice());
                count++;            //计数器
                goods.notify();     //通知消费者来购买
            }else {   //有库存状态,通知消费者去购买
                try {
                    goods.wait();       //生产商有库存,陷入等待状态,等消费者购买
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

}

public class Demo1 {
    public static void main(String[] args) {
        Goods goods = new Goods("小汽车",5.5,false);   //实例化商品
        new Thread(new Cusyomer(goods)).start();     //实例化消费者线程并启动
        new Thread(new Prodector(goods)).start();    //实例化生产者线程并启动
    }
}

线程池

线程池是一个容纳了多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程的对象的操作,无需反复创建线程而消耗更多的资源

问题:创建线程池的方式有几种

1.通过ThreadPoolExecutor手动创建线程池

2.通过Executor执行器自动创建线程池

问题:创建线程池的方法:

1.Executors.newFixedThreadPool创建一个固定大小的线程池,可控制并发线程数,超出的线程会在队列中等待。

2.Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。

3.Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序。

4.Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池。

5.Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池。

6.Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】。

7.ThreadPoolExecutor:手动创建线程池的方式,它创建时最多可以设置 7 个参数。

手动创建线程池的参数含义

最大线程数(MaximumPoolSize):线程池运行的最大线程数量,受属性CAPACITY的限制,最大为(2^29)-1(约5亿)

核心线程数(CorePoolSize):线程池中保持最小活动数的线程数量,并且不允许超时,除非调用allowCoreThreadTimeOut方法,这个时候最小值是0。

多余线程的存活时间(keepAliveTime):存活时间(Keep-alive times):空闲线程等待工作的超时时间(以纳秒为单位)如果当前线程池中的线程数超过了核心线程数,超出的部分线程如果空闲的时长大于存活时长,那么他们将会被终止运行。

时间单位(TimeUnit):TimeUnit 是存活时间的单位。

阻塞队列(BlockingQueue):任何实现了BlockingQueue接口的实现类都可以用来传输和保存提交的任务,阻塞队列的使用和线程池大小相关。

缓冲队列workQueue:当线程数超过了corePoolSize就会进入缓冲队列

线程工程ThreadFactory:用来创建线程。

AbortPolicy线程饱和策略:

        ThreadPoolExecutor.AbortPolicy,默认的拒绝策略,简单粗暴,拒绝的时候直接抛RejectedExecutionException异常


        ThreadPoolExecutor.CallerRunsPolicy,由调用者执行自身execute方法来运行提交进来的任务,从名字CallerRuns(调用者运行)中就可以看出。它会提供一个简单的反馈控制机制,这种策略将降低新任务被提交上来的速度。


        ThreadPoolExecutor.DiscardPolicy,也很简单粗暴,直接丢弃任务,不抛异常。


        ThreadPoolExecutor.DiscardOldestPolicy,DiscardOldest丢弃最早的任务,在队列头部也就是最新进入队列的任务会被丢弃,然后尝试提交新任务,如果提交失败会继续重复以上步骤。
也可以自己实现RejectedExecutionHandler接口,并重写rejectedExecution方法来自定义拒绝策略。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值