1、 程序、进程、线程2、java中如何创建线程?3、Thread类中的方法 Lang包中4、java中的线程分类: 用户线程和守护线程5、多线程
什么是多线程?何时需要线程?多线程优缺点?
如何解决多线程安全问题?
方法一:synchronized 同步的方法二:Lock锁 ReentrantLock 可重入锁 6、线程死锁7、线程通信
sleep()和wait()区别:生产者消费者问题 8、第三种创建线程的方式
Callable接口和Runnable对比: 1、 程序、进程、线程
程序:在计算机上安装的程序代码(静态的代码)
进程:运行中的程序,从硬盘上加载到内存中,分配空间 是操作系统分配空间单位
线程:是进程内部的最小执行单元, 是操作系统调度单位 cpu执行以线程为单位
举例:QQ 安装到电脑上(静态代码) 双击运行:加载到内存中运行
线程、进程的关系:
线程隶属于进程
一个进程中可以有多个线程
一个进程中至少包含一个线程,即主线程
可以在主线程中创建其他线程
main方法:用来启动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();
守护线程就是所有非守护线程的保姆
正常创建的线程是用户线程
守护线程和用户线程功能一样,去完成某件事情
用户线程的工作完成后,它就结束了
守护线程是等待用户线程工作完成后,守护线程才会自动退出
例如:GC垃圾回收器 垃圾回收任务就是在一个守护线程中进行的
怎么创建守护线程?demo4
在一个应用程序中可以有多个线程同时执行任务
何时需要线程? 程序中需要同时执行多个任务时,就需要多个线程
例如: 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锁是靠java代码来控制的
ReentrantLock 类实现Lock接口,可以来控制与synchronized相同的功能, 但是两种的实现细节完全不一样
ReentrantLock,简称Lock Lock加锁只能对某段代码加锁
而且是显示的加锁和释放锁
synchronized是自动加锁和释放锁
两个方法区别: synchronized和Lock锁区别
①synchronized是靠底层指令是实现,Lock锁是靠java代码来控制的
②synchronized是自动加锁和释放锁,Lock加锁只能对某段代码加锁,而且是显示的加锁和释放锁
不同的线程分别占用对方需要的同步资源不放弃
出现死锁,发生死锁后,程序不会报错,只能等待
锁的嵌套时,容易发生死锁现象
加锁时,要考虑清楚锁的顺序,尽量减少锁的嵌套
死锁示例: demo5
线程通信是多个线程之间相互牵制,制约运行
三个方法:
.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();//启动消费者线程 }}
生产者生产了一个商品
消费者取走商品
生产者生产了一个商品
消费者取走商品
生产者生产了一个商品
消费者取走商品
生产者生产了一个商品
消费者取走商品
生产者生产了一个商品
消费者取走商品
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
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
45