Thread
java能够直接开启多线程吗
Jdk提供了Thread类吗? 它不是就是可以吗…
Java不能直接开启多线程,线程是依赖于进程存在的,必须有进程才能线程!
而创建进程—需要创建系统资源 ,Java语言不能够创建系统资源的,只有c语言底层语
言能够操作系统资源,最底层的还是非Java语言实现;只是jdk为了使用方便将一些操作
线程的东西放在了Thread类中,但是里面的部分能也都是非java语言实现
start()---->开启线程的的方法,底层依赖于start0()—被native修饰:非Java语言实现(本地方法—跟系统相关的)
线程的状态
Thread类的内部枚举类型:
public enum State{
规定了6种状态
NEW ,新建
RUNNABLE,运行
BLOCKED,阻塞状态
WAITTING,等待(一直等待)
TIMED_WAITTING,超时等待
TERMINATED 终止死亡状态
}
/**
* @author liutao
* @date 2022/8/28 11:25
*
* 实现线程的创建方式第一种:继承关系
* 实现步骤
* 1)自定义一个类,继承自Thread类 (jdk提供的线程类),
* 这个类就是线程类
* 2)在自定义的类中重写Thread类的run方法
* 3)在main线程(主线程,或者用户线程)
* 创建当前自定义类对象--->就是线程对象,启动线程即可
*
*
* 线程不知道哪一个线程?Thread类提供了一些方法,设置线程名称,获取线程名字
* public final void setName(String name):给线程设置名字
* public final String getName():获取线程名称
*
*/
//自定义一个类继承Thread
public class MyThread extends Thread{
//重写Thread类的run方法
@Override
public void run() {
//写代码:耗时的操作:循环/以后io流读写文件...
for(int x = 0 ;x <200;x++){
//public final String getName():获取线程名称
System.out.println(getName()+":"+x) ;
}
}
}
public final void join() 线程礼让! throws InterruptedException
public static void yield():暂停当前正在执行的线程,并执行其他线程
让多个线程执行更和谐,
但是不能保证在抢占CPU的执行权的时候,A/B线程,A一次,B一次,也就是说明线程的执行随机性大
Thread类常量表:
下面自定义常量
public static final int MAX_PRIORITY 10 最大优先级
public static final int MIN_PRIORITY 1 最小优先级
public static final int NORM_PRIORITY 5 默认优先级
线程的优先级越大,只能说明它抢占到CPU的执行权的几率越大,但是不一定,因为线程的执行具有随机性!
public final void setPriority(int newPriority)设置优先级
/**
* @author liutao
* @date 2022/8/23 15:43
* 创建线程的另一种方式:是实现一个种接口 方式 (推荐使用的,体现数据共享的概念)
* 谁实现了Runable接口的run方法,谁就是资源类!(多个线程必须同时操作同一个资源)
*
* 实现步骤:
* 1)自定义一个类实现Runnable接口,实现里面的run方法---"线程的具体执行的逻辑"
* 2)在主线程中(main)
* 创建实现Runable接口实现类---就是资源类对象
* 创建 Thread类对象
* 使用它完成public Thread(Runnable target,String name)
* 在Thread构造方法中,将资源类对象作为参数传递!(多个线程必须同时操作同一个资源)
* 3)启动线程 start()
*
*/
//自定义一个类实现Runnable即可
public class MyRunnable implements Runnable{
@Override
public void run() {
for(int x = 0 ; x <100;x++){
//如何获取线程名字?
//Thread类提供静态功能:哪个线程进来,获取当前正在执行的线程,就获取线程名称
//public static Thread currentThread()
//public String getName():获取线程名字
// System.out.println(getName()+":"+x);//getName()继承Thread类才能用,现在不是继承关系
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
线程操作的时候:目前现在两种方式,继承关系,实现Runnable接口关系
为什么接口的方式要好一些?
1)java面向接口编程,接口的提供的抽象方法,实现类必须实现这个方法
2)实现Runable接口的方式,体现数据共享的概念
SellTicket st = new SellTicket() ;//栈内存–指向堆内存地址
//创建三个线程
Thread t1 = new Thread(st,“窗口1”) ; //三个线程 都在操作同一个资源类st对象
Thread t2 = new Thread(st,“窗口2”) ;
Thread t3 = new Thread(st,“窗口3”) ;
3)第二种方式用到了"代理设计模式"
虽然方式2优于方式1,问题:刚才加入了网络延迟睡眠150毫秒,出现负票,也会出现同票(线程不安全!)
需要解决------------------>“Java的同步机制 synchronized 同步锁”,使用同步代码块解决
静态代理
/**
* @author liutao
* @date 2022/8/24 9:56
* 设计模式:Java中23种 ---不是一种技术,它是一种"思想"(java编程语言中,根深蒂固的东西)
* 分为三大类
* 创建型设计模式(对象的创建):单例模式(重点),静态工厂方法模式(重点),工厂方法模式(重点)
* 结构型设计模式(整个结构组成:抽象类,接口,..具体实现类...):代理模式(重点)
* 行为型设计模式(功能型设计模式:使用具体的应用场景):适配器模式,装饰者设计模式(io流中就会用到)
*
*
* 静态代理属于代理设计模式
*
* 代理模式 :核心思想:代理角色帮助真实角色完成一些事情 比如:过年回家排队买火车票
* 静态代理: 代理角色和真实角色都需要实现同一个接口
* 线程的创建方式2:就使用到了静态代理
* class MyRunnable implements Runnable{--->真实角色
* //重写run
*
* //专注于run里面的真实内容
*
*
* }
*
* class Thread implements Runnable{//代理角色:帮助我们MyRunnable完成事情
* private Runnable target;
*
* public Thread(Runnable target, String name) {
*
* //这里面完成 需要target赋值--->MyRunnable实现了Runnable接口
* }
*
*
* //重写run public void run() {
* if (target != null) {
* target.run();//接口变量.run()
* }
* }
* }
*
*
*
* 动态代理
* jdk动态代理(jdk提供的Proxy类) 反射中就会用到
* cglib动态代理(第三方jar包)
*
*
* 举例:
* 结婚这件事情,每个人都需要结婚
*
*
*
* 线程创建的方式2优于方式1的最大好处:使用到了静态代理以及数据共享概念
*/
同步锁synchronized
/**
* @author 刘涛
* @date 2022/8/24 10:51
*
* 在实际开发中,有的时候同步代码块很少用,如果一个方法中的第一句话就是
* synchronzied(锁对象){
* 多条语句对共享数据的操作;
* }
* 将这个synchronized提到方法声明上,形成了同步方法(一般说的非静态的)
*
* 面试题
* 1)同步方法的锁对象是谁呢? (一般说的都是非静态的)
* 非静态同步方法的锁对象是 this:代表当前类对象的地址引用
*
* 2)静态的同步方法的锁对象是谁呢?--->静态的东西和类相关
* 跟反射有关系,是当前类的字节码文件对象
* 之前讲Object类---->Class getClass():获取字节码文件对象 方式1
* 任意java类型的class属性--->第二种方式获取字节码文件对象
*/
生产者和消费者
/**
* @author 刘涛
* @date 2022/8/24 15:01
* 模拟 生成者和消费思想模式,让线程之间的进行互相通信
*
* Student类--->学生数据(看成--->包子)
* name/age 姓名和年龄
* SetThread类---->生产者线程操作的资源类--->给学生的信息赋值
* GetThrad类----->消费者线程操作的资源类---->输出学生的信息
* ThreadDemo类--->主线程main(用户线程)
*
* 需求1:先在SetThread类产生学生对象数据,在GetThrad类输出学生数据
*
* 问题: 发现数据是null 0 ,为什么呢?不同资源类对数据的操作应该同一个数据
* 在SetThread里面new 一个学生对象
* 在GetThread里面new 一个学生对象 ,不是同一个学生对象,没有数据!
* 解决方案,通过构造方法进行数据传递;必须使用同一个学生对象!
*
* 需求2:生产者不断的产生数据,消费者不断的使用数据!---加入循环操作
* 问题:数据紊乱,部分数据的名字和年龄不对应
* 数据一打印一大片,是因为线程的执行的时候,cpu的一点点时间片足够线程执行很多次,数据可能一打印一大片
* 数据紊乱,线程的执行具有随机性
*
* 说明程序不安全------>上午讲了---->同步代码块 (多线程的同步机制 synchronized)
* 校验多线程安全问题的标准:
* 是否是多线程环境 是
* 是否存在共享数据 是
*
* 是否有多条语句对共享数据的操作 有---->使用同步代码块将它包起来
*
* 问题:数据不会紊乱,但是一打印一大片, ,cpu的一点点时间片足够线程执行很多次
* 解决方案: 依次打印------------>使用Java的等待唤醒机制(重点)(等会马上讲)
* 高圆圆 43
* 张杨 30
* 高圆圆 43
* 张杨 30
* ...
* ...
* ...
* wait()和notify()也是同步机制的一种,这两个必须一 块使用的 (解决线程通信)
* 同步机制:synchonzied(锁对象){或者是同步方法
*
* }
*
*/
引入等待唤醒机制(wait() + notify())
使用多线程加入了synchronized,解决安全问题,但是可能出现死锁现象
生产者和消费者模式思想:
生产者不断产数据,消费者不断使用数据
当生产者没有数据,等待产生数据,有数据了,唤醒(通知)消费者线程,来消费
当消费者有数据,等待先使用,没有数据了,唤醒(通知)生产者线程,来产生数据
wait()+notify()本身两个结合使用 ----都在synchronzied代码块中使用,就
属于"同步机制"
代码—昨天生产者SetThread产生学生数据,消费者GetThread使用学生数据
(标准写法)-------->“等待唤醒机制–信号灯法”
代码举例(模板代码)
Student
/**
* @author 刘涛
* @date 2022/8/24 15:04
*
* 学生类
*/
public class Student {
//不加入私有修饰
String name ; //姓名
int age ; //年龄
boolean flag ; //标记:是否有数据,false没有数据;true有数据
//默认值就是false
}
SetThread
/**
* @author 刘涛
* @date 2022/8/24 15:05
*
*
*/
//生产者资源类
public class SetThread implements Runnable {
//声明学生类型变量
private Student s;
private int x = 0 ; //统计变量
public SetThread(Student s){
this.s = s ;
}
@Override
public void run() {
//生产者线程先抢占到了, x = 0
//不断的产生数据
while(true){
//调用学生 类的同步方法:产生数据
synchronized (s){ //锁对象可以是任意Java类对象
//判断
if(s.flag){ //生产者没有数据,等待产生数据
//使用Object提供了一个方法wait()线程等待
try {
s.wait();//锁对象调用
//wait()特点:当被锁对象调用,会立即释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(x%2==0){
//产生学生数据
// Student s = new Student() ;
s.name ="刘涛" ;
s.age = 43 ;//有数据了 ,消费者所在的线程就输出(使用)数据
}else{
s.name = "胡志兰" ;
s.age = 30 ;
}
x++ ;//不断++ x=1
//修改标记 信号灯法
s.flag = true ;//有数据了
//唤醒(通知)消费者线程,赶紧来使用
//锁对象调用者这些方法
s.notify();
}
记录 张杨 ,准备使用年龄赋值30,这个时候,第二个线程消费者抢占到了
//线程的执行随机性,就会直接输出 s.name = "张三" ,s.age = 43 上一次年龄的值
}
}
}
GetThread
/**
* @author 高圆圆
* @date 2022/8/24 15:05
*/
//消费者资源类
public class GetThread implements Runnable{
private Student s ;
public GetThread(Student s){
this.s = s ;
}
@Override
public void run() {
//不断的使用数据(消费数据)
while(true){
//调用学生类的同步方法:产生学生数据
synchronized (s){
//如果有数据,等待消费者先使用数据
if(!s.flag){
try {
s.wait();
//wait()特点:当被锁对象调用,会立即释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//Student s = new Student();
//输出学生数据
System.out.print(s.name+" ");//刘涛
System.out.print(s.age); //43
System.out.println();//换行
//消费完了,没有数据了,修改标记
s.flag = false ;
//唤醒(通知)生成者线程,赶紧来产生数据
s.notify();
}
}
}
}
ThreadDemo
public class ThreadDemo {
public static void main(String[] args) {
//创建一个学生对象
Student s = new Student();//同一个学生对象
//创建生产者资源类对象
SetThread st = new SetThread(s) ;
//创建消费者子资源类对象
GetThread gt = new GetThread(s) ;
//创建线程对象
Thread t1 = new Thread(st) ;
Thread t2 = new Thread(gt) ;
//启动线程
t1.start();
t2.start();
}
}
//ABCDEFGHJK 第一个线程 输出这些字母
//0123456789 第二个线程 输出数字
//展示结果---A0B1C2D3.......----考的就等待唤醒机制
线程池
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author 刘涛
* @date 2022/8/24 16:54
*
* 创建线程的方式有前两种了
* 1)继承关系
* 2)实现Runnable接口的方式,使用到了静态代理
* 为什么要有第三种方式呢?线程池
*
* 使用步骤
* 1)创建线程池 Executors:工厂类(提供一些静态的方法) 专门创建线程池
* public static ExecutorService newFixedThreadPool(int nThreads)创建
* 一个固定的可重用的线程数的线程池
* 返回值是一个接口---->底层原码---肯定返回的接口的子实现类对象
*
* 2)提交异步任务 ExecutorService:线程池---接口里面的方法都要被ThreadPoolExecutor子实现类实现
*
* <T> Future<T> submit(Callable<T> task):提交异步任务,并返回结果处理
* 这个方法的返回值Future接口---代表的具体的处理结果,
* 如果仅仅是看一下多个线程互相抢占CPU执行权的效果,返回值可以不写;
* 如果要进行线程计算结果,必须有返回结果
* 参数:Callable: 接口,需要接口的子实现类对象(执行的任务---类似于Runnable接口)
* allable里面的方法call---->类似于Runnable的run 方法---耗时的操作,线程执行的业务代码
* 3)关闭线程池
* void shutdown():关闭之后,不会接受任何新任务
*
*/
public class ThreadDemo {
public static void main(String[] args) {
//创建线程池
// public static ExecutorService newFixedThreadPool(int nThreads)
ExecutorService es = Executors.newFixedThreadPool(2);//创建2条线程
//提交异步任务结果
/**
* <T> Future<T> submit(Callable<T> task):提交异步任务,并返回结果处理
* 这个方法的返回值Future接口---代表的具体的处理结果,
* 如果仅仅是看一下多个线程互相抢占CPU执行权的效果,返回值可以不写;
* 如果要进行线程计算结果,必须有返回结果
*/
Callable c = new MyCallable() ;//接口多态
es.submit(c) ; //第一次提交异步任务----->相当于---线程对象.start()
es.submit(c) ; //第二此提交异步任务----
//线程池会默认给线程一个name
//关闭线程池
es.shutdown();
}
}