8.1 线程创建、多线程、线程死锁、线程通信、生产者消费者问题 简述

1、 程序、进程、线程

​ 程序:在计算机上安装的程序代码(静态的代码)
​ 进程:运行中的程序,从硬盘上加载到内存中,分配空间 是操作系统分配空间单位
​ 线程:是进程内部的最小执行单元, 是操作系统调度单位 cpu执行以线程为单位

​ 举例:QQ 安装到电脑上(静态代码) 双击运行:加载到内存中运行

​ 线程、进程的关系:
​ 线程隶属于进程
​ 一个进程中可以有多个线程
​ 一个进程中至少包含一个线程,即主线程
​ 可以在主线程中创建其他线程

​ main方法:用来启动java主线程
​ 注意:方法调用不是多线程,程序按照调用的先后顺序执行。

2、java中如何创建线程?

​ ① extends Thread类 重写run()

​ ② implements Runnable 重写run()

​ ③ Callable接口 implements Callable

​ 总结: 线程是一个可以独立被操作系统执行调度的,多核cpu下同时可以执行多个线程

3、Thread类中的方法 Lang包中

​ Thread类是用来管理线程的

​ 构造方法

​ run() 实现线程任务
​ start() 启动线程方法

​ 线程是有优先级的,优先级范围0-10 默认是5
​ final void setPriority(int newPriority) 设置线程的优先级
​ final int getPriority() 返回线程优先级

​ 较高优先级的线程,有更多机会获得cpu的执行权

​ 下面这三个方法会影响到线程状态:
​ join()
​ sleep() 线程休眠指定时间
​ yield() 线程主动让出

​ 线程生命周期和线程状态
​ 生命周期:就是什么时候创建,又什么时候销毁(死亡)
​ 新建:
​ MyThread t = new Thread();

4、java中的线程分类: 用户线程和守护线程

​ 守护线程就是所有非守护线程的保姆
​ 正常创建的线程是用户线程
​ 守护线程和用户线程功能一样,去完成某件事情
​ 用户线程的工作完成后,它就结束了
​ 守护线程是等待用户线程工作完成后,守护线程才会自动退出
​ 例如:GC垃圾回收器 垃圾回收任务就是在一个守护线程中进行的
怎么创建守护线程?demo4

5、多线程

什么是多线程?

​ 在一个应用程序中可以有多个线程同时执行任务

何时需要线程?

​ 程序中需要同时执行多个任务时,就需要多个线程
​ 例如: 360安全卫士 同时执行多个任务:体检 查杀 电脑清理…

多线程优缺点?

​ 多线程优点:
​ ①提高了程序速度
​ ②提高了CPU利用率
​ ③改善了程序结构
​ 多线程缺点:
​ ①多线程对内存消耗增高
​ ②多线程切换执行对CPU的要求也提高了
​ 如何解决? 这两个缺点可以通过升级硬件设备解决
​ ③多个线程访问同一个共享资源,会出现线程安全问题
​ 出现线程安全问题情况:出现多线程 && 访问同一个共享资源 现在都是多核CPU,那么就可以在同一时间点同时处理多个线程
​ 注意: 单一的多线程不会出现线程安全问题, 每一个线程都在做自己的事情,没有交集

​ 线程并行执行: 多个人同一时刻做不同的事情,互不干扰
​ 线程并发执行: 同一时间段,依次执行某件事情 一个一个来,交替做
​ 单核cpu是天然的并发执行,一次只能进入一个,所以不用加锁+排队解决

​ 必须程序控制线程,并发执行 高并发 比如:双十一,秒杀,抢购…
​ 卖票:并发执行 美团,支付宝,电影院 三种方式 2.12万达1号15:00第5排5号

如何解决多线程安全问题?

​ 加锁+排队 为出票方法加锁,一次只能有一个线程进入到出票方法中。

方法一:synchronized 同步的

​ ①
​ synchronized(同步锁){ //synchronized(obj) 锁对象:new Object
​ //需要被同步的代码;
​ }
​ ②
​ //方法加上synchronized修饰 创建了两个个线程任务对象时,必须加上static修饰方法
​ public synchronized void show(Stirng name){
​ //需要被同步的代码
​ }

​ synchronized关键字 同步的
​ synchronized 可以修饰代码块
​ ①synchronized(同步锁对象){

​ }
​ ②synchronized修饰方法 如果是非静态方法,那么锁的对象是this
​ 如果是静态方法,那么锁的对个是类的Class对象(一个类只有一个Class对象)

​ synchronized是靠底层指令是实现
​ synchronized可以修饰代码块,修饰方法,注意锁的对象(锁对象可能会变)
​ sychronized加锁的方式是隐式的,进入到同步代码块时,自动获取锁
​ 同步代码块执行完成后,自动释放锁

方法二:Lock锁 ReentrantLock 可重入锁

​ Lock锁是靠java代码来控制的
​ ReentrantLock 类实现Lock接口,可以来控制与synchronized相同的功能, 但是两种的实现细节完全不一样
​ ReentrantLock,简称Lock Lock加锁只能对某段代码加锁
​ 而且是显示的加锁和释放锁
​ synchronized是自动加锁和释放锁

​ 两个方法区别: synchronized和Lock锁区别
​ ①synchronized是靠底层指令是实现,Lock锁是靠java代码来控制的
​ ②synchronized是自动加锁和释放锁,Lock加锁只能对某段代码加锁,而且是显示的加锁和释放锁

6、线程死锁

​ 不同的线程分别占用对方需要的同步资源不放弃
​ 出现死锁,发生死锁后,程序不会报错,只能等待
​ 锁的嵌套时,容易发生死锁现象
​ 加锁时,要考虑清楚锁的顺序,尽量减少锁的嵌套
​ 死锁示例: demo5

7、线程通信

​ 线程通信是多个线程之间相互牵制,制约运行
​ 三个方法:
​ .wait() 让线程等待,调用wait()方法后,线程进入到阻塞状态,他必须通过另一个线程唤醒 wait必须让别人唤醒,sleep给定时间,时间到了就醒了
​ .notify() 唤醒等待中的线程
​ .notifyall() 唤醒等待中的所有线程
​ 这三个方法必须在synchronized()同步代码块中运行

​ 通过两个线程交替打印1-100之间的数字 demo6

sleep()和wait()区别:

​ 相同点:可以让进程进入阻塞
​ 不同点:
​ sleep() 是Thread类中的方法
​ 不会释放锁
​ 休眠时间到了后,会自动按进入到就绪状态
​ wait() 是Object类中的方法
​ 可以释放锁
​ wait后的线程,需要notify/notifyall唤醒
​ demo6

生产者消费者问题

​ 两个线程之间相互牵制使用

​ 生产者:Productor
​ 消费者:Customer
​ 柜台:Counter

​ 生产者线程:
​ 柜台产品>0
​ 消费者线程:

package com.ffyc.javathread.ProducerConsumerProblem;

/*
    柜台
 */
public class Counter {

    int num = 0;

    /*
        生产者调用   加
        synchronized修饰的是非静态方法,锁对象默认的是this
     */
    public synchronized void jia(){
        if(num == 0){
            num++;
            System.out.println("生产者生产了一个商品");
            this.notify();//唤醒
        }else {
            try {
                this.wait(); //生产者等待  释放锁
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    /*
        消费者调用   减
     */
    public synchronized void jian(){
        if(num == 1){
            num--;
            System.out.println("消费者取走商品");
            this.notify();
        }else {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}
package com.ffyc.javathread.ProducerConsumerProblem;
/*
    生产者
 */
public class Productor extends Thread{

    Counter counter;

    public Productor(Counter c) {
        this.counter = c;
    }

    @Override
    public void run() {
        while (true) {
            counter.jia();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
package com.ffyc.javathread.ProducerConsumerProblem;
/*
    消费者
 */
public class Customer extends  Thread{

    Counter counter;

    public Customer(Counter c) {
        this.counter = c;
    }

    @Override
    public void run() {
        while(true){
            counter.jian();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
package com.ffyc.javathread.ProducerConsumerProblem;

public class Test {

    public static void main(String[] args) {

        Counter c = new Counter(); //共享同一个柜台,共享数据


        Productor p = new Productor(c);
                  p.start();//启动生产线程

        Customer co = new Customer(c);
                 co.start();//启动消费者线程

    }
}

生产者生产了一个商品
消费者取走商品
生产者生产了一个商品
消费者取走商品
生产者生产了一个商品
消费者取走商品
生产者生产了一个商品
消费者取走商品
生产者生产了一个商品
消费者取走商品

8、第三种创建线程的方式

​ Callable接口 可调用的

Callable接口和Runnable对比:

​ extends Thread 和 implement Runnable 最终都是重写run()方法
​ 重写run()方法, 没有返回值,也不能抛出异常,就存在局限性
​ java中推出了一个新的接口 Callable接口 里面重写定义了一个call() 可以有返回值(使用泛型自定义),可以抛出异常

package com.ffyc.javathread.demo8;

import java.util.concurrent.Callable;

public class SumThread implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        Thread.sleep(100);
        for (int i = 1; i < 10; i++) {
            sum+=i;
        }
        return sum;
    }
}
package com.ffyc.javathread.demo8;


import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Test {

    public static void main(String[] args) {
        SumThread sumThread = new SumThread();

        FutureTask<Integer> futureTask  = new FutureTask(sumThread);
        Thread t = new Thread(futureTask);
        t.start();
        try {
            Integer sum =  futureTask.get();//获得线程返回的结果
            System.out.println(sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

45

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

宋大米Pro

感谢小主大赏,留言可进互助群~

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

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

打赏作者

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

抵扣说明:

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

余额充值