线程
程序:为解决某种问题,使用计算机语言编写的一系列指令(代码)的集合。
本章中的程序特指的是静态的,安装在硬盘上代码集合.
进程:运行中的程序(被加载到内存中),是操作系统进行资源分配的最小单位。
线程:进程可以进一步细化为线程,是进程内一个最小执行单元(具体要做的事情),
是cpu进行任务调度的最小单位
运行中的QQ就是一个进程,操作系统会为这个进程非陪内存资源,
一个聊天窗口就认为是一个线程,这多个聊天窗口可以同时被cpu执行,
但是这些聊天窗口属于进程,线程是属于进程的。
早期没有线程,早期cpu执行的时候,是以进程为单位执行,
进程单位还是比较大的,当一个进程运行时,其他的进行就不能执行,
所以后来,将进程中的多个任务,细化为线程
Cpu执行单位,也从进程转为更小的线程。
进程和线程的关系
一个进程中可以包含多个线程
一个线程只能隶属于一个进程,线程不能脱离进程存在
一个进程中至少有一个线程(即主线程)java中的main方法,就是用来启动主线程
在主线程中可以创建并启动其他线程
所有线程都共享进程内存资源
需求:想在java程序中有几件不相关的事情同时有机会执行
可以在java中创建线程,把一些要执行的任务放在线程中执行,
这样的话,都拥有让cpu执行的权利
package threadDemo; public class myThread extends Thread{ private String name; public myThread(String name){ this.name = name; } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(name+"运行:"+i); } } }
package threadDemo; public class text { public static void main(String[] args) { myThread myThread1 = new myThread("茂华哥"); myThread myThread2 = new myThread("小孩哥"); myThread1.start(); myThread2.start(); } }
//run();方法是线程执行任务的方法
//切记不能直接调用run方法,就i是一个普通的方法调用,并不是启动线程
.start();//启动线程
/*
创建一个任务,实现Runable 接口
重写Runable接口中的run方法
*/
//Main方法是用来启动java主线程的
MyTask mytask = new MyTask();
Thread t1 = new Thread(mytask);
t1.start();
在java中创建线程
创建线程方式1:
继承Thread类的方式重写run()
创建自己定义的线程类对象,调用start()启动线程
MyThread extends Thread{
}
创建线程方式2:
类 实现 Runable接口,把这个类不能成为线程,是一个任务类
重写run()
new Thread(任务对象);
调用线程对象的start(), 启动线程
以后用第二种多一些:
- 避免了单继承的局限性,因为java是单继承的,继承了Thread类,就不能继承其他类了
- 更适合多线程共享同一份资源的场景
构造方法
New Thread(Runable runable);接收一个任务对象
New Thread(Runable runable,String name); //接收一个任务对象,并为它起一个名字
setName();
sleep(long m); //让线程休眠指定的时间,毫秒单位
join(); // 让其他线程等待当前线程结束
设置线程优先级 1-10 默认是5
优先级较高的线程有更多的机会抢占cpu
先来先服务(FCFS) 调度算法
短作业优先(SJF)算法
优先级调度算法
高响应比优先调度算法
线程的生命周期
线程生命周期,线程从创建到销毁
这个期间经历5个状态
新建 new Thread(); 处于新建状态,此状态还不能被执行
调用 start() 启动线程,让线程进入到就绪状态,
就绪
获得cpu执行权后,线程进入到cpu执行
运行
运行中的线程可以被切换,回到就绪状态,也可能因为休眠等原因进入到阻塞状态
阻塞
线程休眠时间到了 回到就绪状态
死亡
当线程中所有的任务执行完了,线程也就自动销毁
守护线程
守护线程也是线程中的一种,区别在于它的结束,
如果一个线程是守护线程,那么它会等java中其他的线程任务结束后,自动终止
守护线程是为其他线程提供服务的,例如jvm中的垃圾回收线程,就是一个守护线程
多线程
在一个应用程序中,存在多个线程,不同的线程可以并行的执行任务
多线程优点:
提高程序的处理能力,效率也提高了
提高cpu利用率
大任务的拆分
杀毒软件
垃圾清理
病毒查杀
……
多线程缺点:
线程也是需要占用内存资源和cpu资源(可以提升硬件设备)
多个线程对同一个共享的资源进行访问,会出现线程安全问题
龟兔赛跑中的flag 就是共享资源
加锁
/*
同步对象:对多个线程对应的对象必须是同一个,
用来记录有没有线程进入到同步代码块中的,
在对象的对象头中有一块空间用来记录有没有线程进入到同步代码块
同步对象可以是java中任何类的对象
Synchronized(同步对象){
同步代码块
}
*/
synchronized(this){
}
/*
出票的方法
Sychronized修饰方法,同步对象会有默认的
Sychronized如果修饰的是非静态方法,那么同步对象就是this
Sychronized 如果修饰的是静态方法,那么同步对象就是当前类的Class对象
当前类的Class 对象:一个类加载到内存后,会为这个类创建一个唯一的Class类对象
*/
如何解决多个线程访问同一个共享资源时不出问题?
同步 = 排队 + 锁 一次只能有一个线程访问共享资源
加锁方式1: 使用synchronized关键字修饰代码块 和方法
同步对象:对多个线程对应的对象必须是同一个,
用来记录有没有线程进入到同步代码块中的,
在对象的对象头中有一块空间用来记录有没有线程进入到同步代码块
同步对象可以是java中任何类的对象
Synchronized(同步对象/锁){
同步代码块//一次只允许一个i线程进入
}
修饰方法
- 锁不需要我们提供了,会默认提供锁对象
- Synchronized如果修饰的是非静态方法,锁对象是this
Synchronized如果修饰的是静态方法,锁对象是类的Class对象
一个类只能有一个Class对象
加锁方式2:
使用jdk中提供的ReentrantLock类实现加锁
ReentrantLock只能对某一段代码块加锁,不能对整个方法加锁
Synchronized和ReentrantLock 的区别
相同点:都实现了加锁的功能
不同点:
Synchronized 是一个关键字 , ReentrantLock是一个类
Synchronized 修饰代码块和方法 ,ReentrantLock只能修饰代码块
Synchronized 可以隐式的加锁和释放锁,运行过程中如出现了异常可以自动释放
ReentrantLock 需要手动的添加锁和释放锁,建议在finally代码块中释放锁
/*
Wait(); //让线程等待,自动释放锁,必须要其他的线程唤醒
Notify(); //唤醒等待中的线程(调用wait方法的线程,如果有多个等待,唤醒优先级高的)
Notifyall(); //唤醒所有等待中的线程
//这三个方法都是Object类中定义的方法
这三个方法必须在同步代码块中使用
这三个方法必须通过为锁的对象调用
*/
Wait() 是Object类中的方法
必须在同步代码块中使用
Wait()后可以释放锁,必须等待其他线程唤醒
Sleep()是Thread类中的方法
在任何地方都可以使用
Sleep()不会释放锁,在休眠指定的时间后iu,就会恢复(阻塞à就绪)
生产者/消费者问题
创建线程方式3:实现Callable 接口
重写call() 方法
call() 可以有返回值,可以抛出异常,还可以自定义返回结果的类型
基于线程实现龟兔赛跑的小游戏
package hm1; public class game implements Runnable{ boolean flag = true; @Override public void run() { for (int i = 1; i <= 1000 ; i++) { if (Thread.currentThread().getName().equals("兔子")){ System.out.println("兔子跑了"+i+"步"); if (i == 700){ System.out.println("兔子睡着了"); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } } if (Thread.currentThread().getName().equals("乌龟")){ System.out.println("乌龟跑了"+i+"步"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } if (i == 1000){ if (flag){ System.out.println(Thread.currentThread().getName()+"获胜了"); flag = false; }else { System.out.println(Thread.currentThread().getName()+"跑完了"); } } } } }package hm1; public class test { public static void main(String[] args) { game game = new game(); Thread rabbit = new Thread(game, "兔子"); Thread tortoise = new Thread(game, "乌龟"); rabbit.start(); tortoise.start(); } }