欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

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

时间:2023-06-13
文章目录

1、 程序、进程、线程2、java中如何创建线程?3、Thread类中的方法 Lang包中4、java中的线程分类: 用户线程和守护线程5、多线程

什么是多线程?何时需要线程?多线程优缺点?

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

方法一:synchronized 同步的方法二:Lock锁 ReentrantLock 可重入锁 6、线程死锁7、线程通信

sleep()和wait()区别:生产者消费者问题 8、第三种创建线程的方式

Callable接口和Runnable对比: 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; 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 { @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 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

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。