JAVASE16
一、线程状态
我们在现实生活中,思考问题、发现问题、处理问题,这是一个完成一件事或者处理一个问题经历的中间过程。在程序世界也一样,要完成一件事情线程从出生到消亡会经历一个流程,中间会有不同状态的转换。
我们把线程状态分为五种
- 新生状态(新建状态):使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
- 就绪状态:当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
- 运行状态:如果就绪状态的线程获取 CPU 资源,就可以执行run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
- 阻塞状态:如果一个线程执行了sleep(睡眠)suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。,但是线程并没有结束
- 终止状态:一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
注意:一个线程一旦进入终止状态,是不可逆转的,是无法恢复
如果一个线程阻塞状态解除之后,无法直接恢复运行,而是会直接进入到就绪状态,等待cpu的调动。
这里,我们需要知道让一个线程进入到就绪状态的几种方法:
第一种:调用start()方法
第二种:阻塞结束,恢复就绪状态
第三种:调用yield()方法,礼让线程
第四种:cpu的调用切换
我们还需要知道让一个线程进入阻塞状态的几种方法:
第一种:调用sleep()方法
第二种:调用Join()方法
第三种:调用wait()方法
第四种:IO操作
进入终止状态的几种方法:
第一种:等待线程的自然执行完毕
第二种:添加标识判断(这也是比较推荐的一种)
第三种:调用stop方法(极度不推荐)
①几种阻塞方法
Thread.sleep(ms) 线程睡眠,阻塞指定ms时间值
一个线程执行sleep方法,进入休眠,释放cpu的资源
抱着资源睡觉–>对象的资源|锁
//这里我们做一个倒计时的代码,使用Thread.sleep(ms)
public class LianXi implements Runnable{
public static void main(String[] args) {
new Thread(new LianXi()).start();
}
@Override
public void run() {
for(int i=10;i>=0;i--){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("倒计时"+i);
}
}
}
yield 方法
礼让线程
给了其他线程更多的执行机会,但是不能决定是否其他线程一定执行,要看cpu的调度
public class YieldTest implements Runnable{
public static void main(String[] args) {
YieldTest yd = new YieldTest();
new Thread(yd,"A").start();
new Thread(yd,"B").start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始执行");
Thread.yield(); //礼让
System.out.println(Thread.currentThread().getName()+"结束执行");
}
}
join 插队线程
join() 等待插队线程执行完毕 ,当前线程才能继续执行
join(ms) 最多只等待多少ms时间
public class LianXi10 {
public static void main(String[] args) {
Thread th =new Thread(()->{
for(int i=0;i<=20;i++){
System.out.println(Thread.currentThread().getState()+"运行状态");
}
});
System.out.println(th.getState());
th.start();
System.out.println(th.getState());
while(true){
}
}
}
class Fu implements Runnable {
@Override
public void run() {
System.out.println("爹想抽烟了");
System.out.println("给阿耶买烟去,小兔崽子");
Thread th = new Thread(new Zi());
th.start();//注意start方法要写在join方法上面
try {
th.join();//在这里插队,这里是插到Zi执行完毕,也可以设置插的时间
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("烟雾缭绕,不错,爹舒服了");
}
}
class Zi implements Runnable{
@Override
public void run() {
for(int i=1;i<=10;i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("小马阿小二郎,拿着钱袋买烟啷"+"走了"+i+"秒");
}
}
}
②getState()
Thread类中提供了一个属性: getState() 获取一个线程当前的状态
返回Thread.State枚举类型
public class StateTest {
public static void main(String[] args) {
Thread th1 = new Thread(()->{
for(int i=1;i<=20;i++){
if(i==10){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
});
System.out.println(th1.getState()); //NEW
th1.start();
System.out.println(th1.getState()); //RUNNABLE
//不停的打印th1线程的状态
while(true){
System.out.println(th1.getState());
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
//当th1执行完毕进入终止状态结束打印
if(th1.getState().equals(Thread.State.TERMINATED)){
System.out.println(th1.getState());
break;
}
}
}
}
③线程的优先级
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照 线程的优先级 决定应调度哪个线程来执行。线程的优先级用数字表示,范围从 1 到 10
每一个线程都存在优先级 : 优先执行的可能性
默认么一个线程优先级别为5
级别: 1~10 1最小 10最大
static int MAX_PRIORITY 线程可以拥有的最大优先级。
static int MIN_PRIORITY 线程可以拥有的最低优先级。
static int NORM_PRIORITY 分配给线程的默认优先级。
Thread 提供getPriority() 获取一个线程的优先级以及setPriority() 设置一个线程的优先级
注意: 优先级只能放大执行的机会,不代表一定先执行或者后执行优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高后调用优先级低的线程。
public class PriorityTest {
public static void main(String[] args) {
Thread th1 = new Thread(()->{
System.out.println("th1线程");
});
Thread th2 = new Thread(()->{
System.out.println("th2线程");
});
Thread th3 = new Thread(()->{
System.out.println("th3线程");
});
//设置优先级
th1.setPriority(Thread.MAX_PRIORITY);
th2.setPriority(1);
th3.setPriority(5);
//获取优先级
System.out.println(th1.getPriority());
System.out.println(th2.getPriority());
System.out.println(th3.getPriority());
th1.start();
th2.start();
th3.start();
}
}
二、守护线程与用户线程
守护线程与用户线程区别:
守护线程,就是用来守护用户线程的,当用户线程执行完毕,守护线程会直接结束
创建的线程默认用户线程
垃圾回收器就是一个典型的守护线程
如何设置一个线程为守护线程: void setDaemon(boolean on) 将此线程标记为 daemon线程或用户线程。 true->守护线程 false->用户线程
先设置守护线程,然后再start()
public class OtherDemo06 {
public static void main(String[] args) {
Thread th = new Thread(()->{
int i = 1;
while(true){
System.out.println("我是守护线程"+i++);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//设置守护线程
th.setDaemon(true);
//开启线程
th.start();
//主线程默认用户线程
for(int i=1;i<=20;i++){
System.out.println("main用户线程"+i);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
三、中断标识
void interrupt() 为一个线程添加一个中断标识
static boolean interrupted() interrupted方法判断当前线程已经被添加过中断标识(是否当前线程调用过interrupt()方法).同时复位中断标识
boolean isInterrupted() 判断线程是否添加过中断标识,但是不会清楚这个标识
以上三个方法都不能实现线程的终止,只是添加|判断标识,配合break,return…一起使用
如果一个线程执行完毕,会自动复位|清除终止标识
public class InterruptTest {
public static void main(String[] args) {
Thread th = new Thread(()->{
int i = 1;
while(true){
System.out.println("我是th线程"+i++);
if(Thread.interrupted()==true){
System.out.println("结束");
System.out.println("结束之前最后判断状态"+Thread.currentThread().isInterrupted());
break;
}
}
});
th.start();
System.out.println(th.isInterrupted());
//为th线程添加一个中断标识
th.interrupt();
//isInterrupted() 判断th线程是否存在中断标识
for(int i=1;i<=10;i++){
System.out.println(th.isInterrupted());
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
四、线程安全
线程安全:
在一般情况下,创建一个线程是不能提高程序的执行效率的,所以要创建多个线程。但是多个线程同时运行的时候可能调用线程函数,在多个线程同时对同一个内存地址进行写入,由于CPU时间调度上的问题,写入数据会被多次的覆盖,所以就有可能造成数据的不准确。
加锁方式控制安全
同步锁 synchronized -> 我们也称悲观锁
synchronized :
同步方法 : 方法上synchronized修饰
同步块 : synchronized(this|类|资源){排队执行的代码}
类.class : 相当于同步的整个类,class对象是唯一的
this : 同步了这个类型的某一个对象,锁住了这个对象的所有资源
资源 : 当只想要锁住一个对象的某一个资源,可以只锁资源
同步的范围代码太小容易锁不住,范围大效率低
同步方法
同步的资源–>指代调用成员方法的对象
成员是属于对象的
静态的属于类的
简单,但是范围大效率低,而且有可能逻辑出现问题(案例中在死循环的外面)
①同步方法
public class Web12306_01 implements Runnable {
int tickets = 100; //票
/*
买票过程
*/
@Override
public synchronized void run() {
while(true){
//A B C
if(tickets<=0){
break;
}
//A B C
System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--+"张票");
//模拟网络延迟
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Web12306_01 web = new Web12306_01();
//创建3个线程
new Thread(web,"zhangsan").start();
new Thread(web,"lisi").start();
new Thread(web,"wangwu").start();
}
}
②同步块(类)
同步块
synchronized(this|类.class|资源){}
类.class : 类的class对象,每一个类,在加载到内存的时候,都会存在一个属于当前这个类的class对象,唯一的,不变的
简单,但是一个类型的class对象是唯一的
public class Web12306_02 implements Runnable {
int tickets = 10000; //票
/*
买票过程
*/
@Override
public void run() {
while(true){
//A B C
synchronized (Web12306_02.class){//同步类
if(tickets<=0){
break;
}
//A B C
System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--+"张票");
//模拟网络延迟
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Web12306_02 web = new Web12306_02();
Web12306_02 web2 = new Web12306_02();
//创建3个线程
new Thread(web,"zhangsan").start();
new Thread(web,"lisi").start();
new Thread(web2,"wangwu").start();
}
}
③同步块(this)
synchronized(this|类.class|资源){}
this-> 成员方法中默认指代当前调用成员方法的对象
public class Web12306_03 implements Runnable {
int tickets = 50; //票
/*
买票过程
*/
@Override
public void run() {
while(true){
//A B C
synchronized (this){
if(tickets<=0){
break;
}
//A B C
System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--+"张票");
//模拟网络延迟
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Web12306_03 web = new Web12306_03();
Web12306_03 web2 = new Web12306_03();
//创建3个线程
new Thread(web,"zhangsan").start();
new Thread(web2,"lisi").start();
new Thread(web2,"wangwu").start();
}
}
④同步块(资源)
资源
自定义引用数据类型的对象–>new的对象的地址,不变的
锁要锁不变的内容,会变的锁不住
public class Web12306_04 implements Runnable {
Ticket tickets = new Ticket(); //票
/*
买票过程
*/
@Override
public void run() {
while(true){
//A B C
synchronized (tickets){
if(tickets.num<=0){
break;
}
//A B C
System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets.num--+"张票");
//模拟网络延迟
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Web12306_04 web = new Web12306_04();
Web12306_04 web2 = new Web12306_04();
//创建3个线程
new Thread(web2,"zhangsan").start();
new Thread(web2,"lisi").start();
new Thread(web2,"wangwu").start();
}
}
class Ticket{
int num = 50;
}
⑤提高线程效率
双重检查 double check
尽量缩小同步块的代码范围,提高效率
public class Web12306_05 implements Runnable {
int tickets = 10000; //票
/*
买票过程
*/
@Override
public void run() {
while(true){
//A B C
if(tickets<=0){
break;
}
//但我们要执行这些代码,我们完全没必要放入同步块中
System.out.println("------------------------->");
System.out.println("------------------------->");
System.out.println("------------------------->");
System.out.println("------------------------->");
System.out.println("------------------------->");
System.out.println("------------------------->");
//A B C
synchronized (this) {
if(tickets<=0){//我们完全可以在这里再添加一个判断语句,来提高效率
break;
}
System.out.println(Thread.currentThread().getName() + "正在购买第" + tickets-- + "张票");
//模拟网络延迟
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Web12306_05 web = new Web12306_05();
Web12306_05 web2 = new Web12306_05();
//创建3个线程
new Thread(web,"zhangsan").start();
new Thread(web,"lisi").start();
new Thread(web,"wangwu").start();
}
}
五、线程通信
线程通信
Object 类中提供的wait() 与 notify() notifyAll()方法
前提需要使用在同步环境下
wait() 等待,会对应进入到某一个对象的等待池中进行等待,阻塞状态,等待被唤醒
释放cpu的资源,也会释放对象的锁
notify() 唤醒,唤醒某一个对象等待池中正在等待的线程,唤醒某一个
不会释放对象锁
notifyAll() 唤醒全部
这里我们来做个练习:
模拟街道的红绿灯 ,人车公用街道
街道Street 红绿灯boolean flag ns南北 -> 车 we东西 -> 人
人 we东西 true
公用资源 街道
车 ns南北 false
生产者消费者模式–> 信号灯法
public class Demo06 {
public static void main(String[] args) {
Street s = new Street();
new Thread(new Person(s)).start();
new Thread(new Car(s)).start();
}
}
//人
class Person implements Runnable{
private Street street ;
public Person(Street street) {
this.street = street;
}
@Override
public void run() {
while(true){
street.we();
}
}
}
//车
class Car implements Runnable{
private Street street ;
public Car(Street street) {
this.street = street;
}
@Override
public void run() {
while(true){
street.ns();
}
}
}
//街道
class Street{
boolean flag = false; //红绿灯
//南北
public synchronized void ns(){
//车 ns南北 false
if(flag==false){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("车走");
//变灯
flag = true;
//唤醒this对象等待池中的线程
this.notify();
//进入到this对象的等待池中等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//东西
public synchronized void we(){
//we东西 true
if(flag==true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("人走");
//变灯
flag = false;
//唤醒this对象等待池中的线程
this.notify();
//进入到this对象的等待池中等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}