广州网站建设丿新科送推广,用dedecms做的网站,抖音关键词排名系统,免费网站正能量首先我们知道了什么是线程#xff0c;以及线程的实现方法#xff0c;接下来我们来了解一下继承Thread类和Runnable接口的区别#xff0c;其实本质上是没有区别的#xff0c;因为Thread也实现了Runnable的接口#xff0c;唯一区别就是使用Runnable接口#xff0c;可以实现…首先我们知道了什么是线程以及线程的实现方法接下来我们来了解一下继承Thread类和Runnable接口的区别其实本质上是没有区别的因为Thread也实现了Runnable的接口唯一区别就是使用Runnable接口可以实现多个线程共享一个资源的情况而且不会受到单继承的限制这里我们建议使用Runnable接口。
我们再来聊一下线程终止的操作线程终止顾名思义就是想让进程停止运行我们可以通过设置变量的方法来使线程退出即通知方式这里我们用一个实例来进行演示
public class ift {public static void main(String[] args) {Preson2 preson2 new Preson2(true);Thread thread new Thread(preson2);thread.start();for (int i 0; i 10; i){try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(hi~);if (i 5){preson2.setf(false);}}}
}class Preson2 implements Runnable{public Boolean b;public Preson2(Boolean b) {this.b b;}Overridepublic void run() {while(b){try {Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(hello~);}}public void setf(Boolean b){this.b b;}
} 这里我们发现hello只被执行了五次是因为我们在主线程中设定了终止信息才实现了该功能
线程还有一系列的相关方法 这是线程的常用方法我们就不一一列举了。
这里提示一下interrupt方法并不是中断一个线程的运行而是让正在休眠的线程提前中断休眠让它重新运作。
这里有俩个非常常用的方法一个是yield线程的礼让一个是join线程插队线程的礼让让出cpu给其他线程先运行但是会根据cpu的运行状态来确定如果cpu的资源很丰富则不会礼让成功所以线程礼让是不确定的它不一定会成功线程插队线程插队跟线程礼让不同它比较霸道一旦插队成功是必须先执行完该线程才会将cpu让出来给其他线程使用。
join方法
public class ift {public static void main(String[] args) {Thread t3 new Thread(new T3());//创建子线程for (int i 1; i 10; i) {System.out.println(hi i);if(i 5) {//说明主线程输出了 5 次 hit3.start();//启动子线程 输出 hello... t3.join();//立即将 t3 子线程插入到 main 线程让 t3 先执行try {t3.join();} catch (InterruptedException e) {throw new RuntimeException(e);}}try {Thread.sleep(1000);//输出一次 hi, 让 main 线程也休眠 1s} catch (InterruptedException e) {throw new RuntimeException(e);}}}}class T3 implements Runnable{private int count 0;Overridepublic void run() {while (true) {System.out.println(hello (count));try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if (count 10) {break;}}}
} yield方法
public class ift {public static void main(String[] args) {Thread t3 new Thread(new T3());//创建子线程for (int i 1; i 10; i) {System.out.println(hi i);if(i 5) {//说明主线程输出了 5 次 hit3.start();//启动子线程 输出 hello... t3.join();//立即将 t3 子线程插入到 main 线程让 t3 先执行t3.yield();}try {Thread.sleep(1000);//输出一次 hi, 让 main 线程也休眠 1s} catch (InterruptedException e) {throw new RuntimeException(e);}}}}class T3 implements Runnable{private int count 0;Overridepublic void run() {while (true) {System.out.println(hello (count));try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if (count 10) {break;}}}
} 这里我们发现并没有礼让成功而是交替输出。
接下来我们看一下线程的状态图
Runnable状态在jvm机中又被分为ready和running两个状态。
我们可以通过getState()这个方法来查询线程的当前状态我们写个实例来看一下
public class ift {public static void main(String[] args) throws InterruptedException {T t new T();System.out.println(t.getName() 状态 t.getState());//NEWt.start();while (Thread.State.TERMINATED ! t.getState()) {System.out.println(t.getName() 状态 t.getState());//TIMED_WAITING,原因线程每运行一次就休眠一秒钟Thread.sleep(500);}System.out.println(t.getName() 状态 t.getState());//TERMINATED}
}
class T extends Thread {Overridepublic void run() {while (true) {for (int i 0; i 10; i) {System.out.println(hi i getState());//RUNNABLEtry {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}break;}}
}
while (Thread.State.TERMINATED ! t.getState()) {System.out.println(t.getName() 状态 t.getState());//TIMED_WAITING,原因线程每运行一次就休眠一秒钟Thread.sleep(500);
}
该循环内大家肯定有疑问为什么一直都是TIMED_WAITING状态为什么刚启动后的RUNNABLE状态打印不了那是因为运行的过程很快可能就几毫秒所以根本捕捉不到该状态但是休眠的时间很长所以打印的都是休眠的状态运行状态可以在它运行的时候一块打印出来就像我写的一样给大家看一下结果 当我们休眠状态足够短的时候我们发现状态就会发生变化
public class ift {public static void main(String[] args) throws InterruptedException {T t new T();System.out.println(t.getName() 状态 t.getState());//NEWt.start();while (Thread.State.TERMINATED ! t.getState()) {System.out.println(t.getName() 状态 t.getState());//TIMED_WAITING,原因线程每运行一次就休眠一秒钟Thread.sleep(2);}System.out.println(t.getName() 状态 t.getState());//TERMINATED}
}
class T extends Thread {Overridepublic void run() {while (true) {for (int i 0; i 10; i) {System.out.println(hi i getState());//RUNNABLEtry {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}break;}}
} 接下来我们来学习一下线程的同步机制什么是线程的同步机制我们先来看一个实例来引出线程同步机制的作用
public class Tick {public static void main(String[] args) {sellTcik sellTcik new sellTcik();new Thread(sellTcik).start();new Thread(sellTcik).start();new Thread(sellTcik).start();}}class sellTcik implements Runnable{SuppressWarnings({all})public int tick 100;Object object new Object();private Boolean loop true;public void run(){while(loop){m();}}public void m(){if (tick 0) {System.out.println(售票结束);loop false;return;}System.out.println(线程 Thread.currentThread().getName() (--tick));try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
当我们运行代码的时候我们就会发现一些问题 它的票数可能出现负数的情况这是为什么呢这是因为它是三个线程一块进入进行操作的假如票还有两张三个线程一块进去检测票数确实大于0所以会直接拿去卖买了三张所以出现 了超票的情况那我们怎么来解决这样的情况呢这里就要用到我们的线程同步机制了它的实现方法是synchronized括号内可以添加一个对象相当于一把锁只有拿到锁的线程才能进入我们的方法中但是听起来效率会大大降低线程的深入学习以后会进行讨论我们先来了解目前学习的方法而且这个锁有个特点它对于线程而言必须指向的同一个对象如果是不同对象那该锁则是无效的这里我们使用接口类中的Object类型的对象因为实现Runnable接口只需要创建一个对象即可如果我们用的是继承Thread的方法时我们需要创建多个对象这是我们可以将Object类型设置为静态属性这里可以给多线程共享相同的资源。所以object对象肯定是相同对象synchronized被该方法框起来的代码越少越好可以提升效率。
public class Tick {public static void main(String[] args) {sellTcik sellTcik new sellTcik();new Thread(sellTcik).start();new Thread(sellTcik).start();new Thread(sellTcik).start();}}class sellTcik implements Runnable{SuppressWarnings({all})public int tick 100;Object object new Object();private Boolean loop true;public void run(){while(loop){m();}}public void m(){synchronized (object) {if (tick 0) {System.out.println(售票结束);loop false;return;}System.out.println(线程 Thread.currentThread().getName() (--tick));try {Thread.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
} 如果设置同步方法 public synchronized static void m1() { } 它默认的锁就是该类的类名.class 这里我们发现超票的问题得到了很好的解决。同步机制的互斥锁大多设置为自己的类即类名.class)该锁是必然有效的因为类只能有一个可以根据自己的喜好自行添加锁。
互斥锁
互斥锁的特征就像上述所说只允许一个线程拿到锁等到该线程运行完才能让其他线程进入而且互斥锁必须指向的同一对象如果不同对象进入拿到的锁不同则无法做到限制的作用则该锁无效。
线程死锁
我们直接模拟死锁给大家一个直观的感受
public class Tick {public static void main(String[] args) {//模拟死锁现象DeadLockDemo A new DeadLockDemo(true);A.setName(A 线程);DeadLockDemo B new DeadLockDemo(false);B.setName(B 线程);A.start();B.start();}}class DeadLockDemo extends Thread {static Object o1 new Object();// 保证多线程共享一个对象,这里使用 staticstatic Object o2 new Object();boolean flag;public DeadLockDemo(boolean flag) {//构造器this.flag flag;}Overridepublic void run() {//1. 如果 flag 为 T, 线程 A 就会先得到/持有 o1 对象锁, 然后尝试去获取 o2 对象锁//2. 如果线程 A 得不到 o2 对象锁就会 Blocked//3. 如果 flag 为 F, 线程 B 就会先得到/持有 o2 对象锁, 然后尝试去获取 o1 对象锁//4. 如果线程 B 得不到 o1 对象锁就会 Blockedif (flag) {synchronized (o1) {//对象互斥锁, 下面就是同步代码System.out.println(Thread.currentThread().getName() 进入 1);synchronized (o2) { // 这里获得 li 对象的监视权System.out.println(Thread.currentThread().getName() 进入 2);}}} else {synchronized (o2) {System.out.println(Thread.currentThread().getName() 进入 3);synchronized (o1) { // 这里获得 li 对象的监视权System.out.println(Thread.currentThread().getName() 进入 4);}}}}
} 我们发现AB两线程都处于了Blocked状态这就是死锁互相拿着对方需要的锁无法继续往下运行。