Java——多线程(中)

一 (线程的使用)

(!!!都是Thread类中的方法!!!)

(stop,run,start,sleep,yield,join,this.getName,Thread.currentThread().getName,thread.setName,isAlive)

1 终止线程

(用while循环,把run方法执行的动作写里面,再写一个控制线程停止的方法)

(一般不使用JDK提供的stop()/destroy()方法------>被JDK废弃------>直接杀死,结束没有后续了(不人性化))

(boolean型的终止变量------>置为false时------>线程终止------>(把自己该做的做完再终止))

(Thread.sleep();线程休眠------>立即阻塞,静态方法 , System.in.read();会线程阻塞)

终止线程我们一般不使用JDK提供的stop()/destroy()方法(它们本身也被JDK废弃了)。通常的做法是提供一个boolean型的终止变量,当这个变量置为false,则终止线程的运行。

终止线程的典型方法

java 复制代码
public class StopThread implements Runnable {
    
  private boolean flag = true;				//相当于生死牌
    
    
  //重写run方法  
  @Override
  public void run() {
    System.out.println(Thread.currentThread().getName()+" 线程开始");
      int i= 0;
      while(flag){
        System.out.println(Thread.currentThread().getName()+" "+i++);
        try {
          Thread.sleep(1000);				//线程休眠,下面详细讲
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
       }
    System.out.println(Thread.currentThread().getName()+" 线程结束");
   }
    
    
  //控制生死牌的方法  
  public void stop(){
    this.flag = false;
   }


  public static void main(String[] args)throws Exception {
    System.out.println("主线程开始");
    StopThread st = new StopThread();
    Thread t1 = new Thread(st);
    t1.start();
    System.in.read();
    st.stop();
    System.out.println("主线程结束");
   }
}

2 线程休眠

(Thread.sleep();静态方法------>线程休眠------>立即阻塞------>时间到了,解除阻塞)

(单位毫秒)

(方法会报编译异常

------>sleep方法里面抛出了异常(时间不能为负数)

------>只能try-catch出去不能抛

------>因为接口里的run方法没有抛(子类不能抛比父类范围大的),父类没有抛,子类也不能抛)

sleep()方法:可以让正在运行的线程进入阻塞状态,直到休眠时间满了,进入就绪状态。sleep方法的参数为休眠的毫秒数。

java 复制代码
public class SleepThread implements Runnable {
    
    
  @Override
  public void run() {
    System.out.println(Thread.currentThread().getName()+" 线程开始");
      for(int i=0;i<20;i++){
        System.out.println(Thread.currentThread().getName()+" "+i);
        
          
        //线程休眠1秒  
        try {
          Thread.sleep(1000);
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
       }
    System.out.println(Thread.currentThread().getName()+" 线程结束");
   }


    
  public static void main(String[] args) {
    System.out.println("主线程开始");
    Thread t = new Thread(new SleepThread());
    t.start();
    System.out.println("主线程结束");
   }
}

3 线程让步

(yield();静态方法------>让在运行的线程------>就绪状态,给相同优先级的其他线程得到运行机会)

(和sleep有着本质的区别------>sleep是让运行状态回到阻塞状态,阻塞解除再就绪状态------>yield是让运行状态回到就绪状态)

yield()让当前正在运行的线程回到就绪状态,以允许具有相同优先级的其他线程获得运行的机会。因此,使用yield()的目的是让具有相同优先级的线程之间能够适当的轮换执行。但是,实际中无法保证yield()达到让步的目的,因为,让步的线程可能被线程调度程序再次选中

使用yield方法时要注意的几点:

  • yield是一个静态的方法。
  • 调用yield后,yield告诉当前线程把运行机会交给具有相同优先级的线程。
  • yield不能保证,当前线程迅速从运行状态切换到就绪状态。
  • yield只能是将当前线程从运行状态转换到就绪状态,而不能是等待或者阻塞状态。
java 复制代码
public class TestyieldThread implements Runnable {
    
  @Override
  public void run() {
    for(int i=0;i<30;i++){
       
      //如果第一次执行的是Thread-0,就让步,让步一次
      if("Thread-0".equals(Thread.currentThread().getName())){
        if(i == 0){
          Thread.yield();
         }
       }
        
      System.out.println(Thread.currentThread().getName()+" "+i);
     }
   }


  public static void main(String[] args) {
    Thread t1 = new Thread(new TestyieldThread());
    Thread t2 = new Thread(new TestyieldThread());
    t1.start();
    t2.start();
   }
}

4 线程联合

(join();方法------>调用谁的join方法,谁结束被联合的,把联合的阻塞)

(联合之前线程之间是并行,联合之后线程之间是串行)

(与!方法的调用!相似------>联合线程等待被联合的线程)

(会有编译异常try-catch掉)

(想做线程的联合必须有线程的对象)

当前线程邀请调用方法的线程优先执行,在调用方法的线程执行结束之前,当前线程不能再次执行。线程A在运行期间,可以调用线程B的join()方法,让线程B和线程A联合。这样,线程A就必须等待线程B执行完毕后,才能继续执行。

join方法的使用

join()方法就是指调用该方法的线程在执行完run()方法后,再执行join方法后面的代码,即将两个线程合并,用于实现同步控制。

主线程联合A线程,A线程再联合B线程

java 复制代码
//A线程
class A implements Runnable{
  
  //必须要有联合的线程对象
  private Thread b;
  public A(Thread b){
    this.b = b;
   }
    
  //重写run
  @Override
  public void run() {
    for(int i=0;i<10;i++){
      System.out.println(Thread.currentThread().getName()+"  A  "+i);
        
      //join  
      if(i == 5){
        try {
          this.b.join();
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
       }
      //sleep
      try {
        Thread.sleep(1000);
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
   }
}


//B线程
class B implements Runnable{
    
  //重写run  
  @Override
  public void run() {
    for(int i=0;i<20;i++){
      System.out.println(Thread.currentThread().getName()+" B "+i);
      try {
        Thread.sleep(1000);
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
   }
}



//主线程
public class TestJoinThread {
  public static void main(String[] args) {
      
    Thread t1 = new Thread(new B());
    Thread t = new Thread(new A(t1));
    t.start();
    t1.start();
    
      
      
    for(int i=0;i<10;i++){
      System.out.println(Thread.currentThread().getName()+" "+i);
        
      //join
      if(i ==2){    						//当主线程i为2时,联合线程
        try {			
          t.join();							//在主线程当中联合子线程
         } catch (InterruptedException e) {
          e.printStackTrace();
         }
       }
        
      //sleep        
      try {
        Thread.sleep(1000);
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
   }
}

5 线程联合案例

需求:

实现爸爸让儿子买烟------>只有儿子把烟买回来了,爸爸抽烟的线程才能继续

java 复制代码
/**
 * 儿子买烟线程
 */
class SonThread implements Runnable{


  @Override
  public void run() {
    System.out.println("儿子出门买烟");
    System.out.println("儿子买烟需要10分钟");
      
    //模拟买烟  
    for(int i=0;i<10;i++){
      System.out.println("第"+i+"分钟");
      try {
        Thread.sleep(1000);
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
    System.out.println("儿子买烟回来了");
   }
}


/**
 * 爸爸抽烟线程
 */
class FatherThread implements Runnable{


  @Override
  public void run() {
    System.out.println("爸爸想抽烟,发现烟抽完了");
    System.out.println("爸爸让儿子去买一包红塔山");
    
    //启动儿子买烟线程
    Thread t = new Thread(new SonThread());
    t.start();
    System.out.println("等待儿子买烟回来");
      
    try {
      t.join();
     } catch (InterruptedException e) {
      e.printStackTrace();
      System.out.println("爸爸出门找儿子");			//如果出异常了,爸爸出去找儿子,结束线程。
      System.exit(1);								//结束虚拟机
     }
    System.out.println("爸爸高兴的接过烟,并把零钱给了儿子");
   }
}


public class TestJoinDemo {
  public static void main(String[] args) {
    System.out.println("爸爸和儿子买烟的故事");
    Thread t = new Thread(new FatherThread());
    t.start();
   }
}

6 获取线程名称

(继承Thread------>this.getName())

(实现Runnable------>Thread.currentThread().getName())

(------>Thread.currentThread()静态方法,返回当前线程对象,然后再继续用静态方法得到名字)

方式一

this.getName()获取线程名称,该方法适用于继承Thread实现多线程方式。

java 复制代码
class GetName1 extends Thread{
  @Override
  public void run() {
    System.out.println(this.getName());
   }
}

方式二

Thread.currentThread().getName()获取线程名称,该方法适用于实现Runnable接口实现多线程方式。

java 复制代码
class GetName2 implements Runnable{


  @Override
  public void run() {
    System.out.println(Thread.currentThread().getName());
   }
}

7 修改线程名称

(继承Thread------>提供有参的构造方法,super给Thread------>创建对象时通过参数设置)

(实现Runnable------>创建对象时已经通过Thread类把线程包装了------>之间用thread.setName())

(------>启动线程之前修改名字)

(实现Runnable------>通过Thread的构造方法)

方式一

通过构造方法设置线程名称。

java 复制代码
class SetName1 extends Thread{
    
  public SetName1(String name){
    super(name);					//把线程名字交给Thread父类
   }
    
  @Override
  public void run() {
    System.out.println(this.getName());
   }
}


public class SetNameThread {
  public static void main(String[] args) {
    SetName1 setName1 = new SetName1("SetName1");		//创建对象时候给名字参数
    setName1.start();
   }
}

方式二

通过setName()方法设置线程名称。

java 复制代码
class SetName2 implements Runnable{


  @Override
  public void run() {
    System.out.println(Thread.currentThread().getName());
   }
}


public class SetNameThread {
  public static void main(String[] args) {
    Thread thread = new Thread(new SetName2());
    thread.setName("SetName2");
    thread.start();
   }
}

方式三

通过构造方法

java 复制代码
class SetName3 implements Runnable{
    
  @Override
  public void run() {
    
      System.out.println(Thread.currentThread().getName());
     
   }
}



public class PriorityThread {
  public static void main(String[] args)throws Exception {
      
    SetName3 p1 = new SetName3();
    SetName3 p2 = new SetName3();
    Thread t1 = new Thread(p1,"线程1");
    Thread t2 = new Thread(p2,"线程2"); 
    t1.start();
    t2.start();
   
   }
}

8 判断线程是否存活

(isAlive()方法------> 判断当前线程是否活动状态------>就绪,运行,阻塞(除了新生和死亡))

isAlive()方法: 判断当前的线程是否处于活动状态。

活动状态是指线程已经启动且尚未终止,线程处于正在运行或准备开始运行的状态,就认为线程是存活的。

java 复制代码
class Alive implements Runnable{


  @Override
  public void run() {  
    for(int i=0;i<4;i++){
      System.out.println(Thread.currentThread().getName()+" "+i);
        
      //sleep  
      try {
        Thread.sleep(500);
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
   }
}


public class TestAliveThread {
  public static void main(String[] args) {
    
    //实例化  
    Thread thread = new Thread(new Alive());
    //设置名称  
    thread.setName("Alive");
    //启动之后线程活动  
    thread.start();
    System.out.println(thread.getName()+" "+thread.isAlive());//返回false
      
    try {
      Thread.sleep(4000);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
    //子线程结束  
    System.out.println(thread.getName()+" "+thread.isAlive());//返回false
   }
}

二 (线程的优先级)

1 什么是优先级

(虽然说在Java有的多线程中,线程优先级的设计的初衷是很好的------>但是最终会受到操作系统的限制,而未必能够达到当前的效果------>与操作系统有关------>所以优先级越高,被执行的!!!概率越高!!!(不是绝对))

(范围1--->10,数值越大优先级越高,数值越小优先级越低)

每一个线程都是有优先级的,我们可以为每个线程定义线程的优先级,但是这并不能保证高优先级的线程会在低优先级的线程前执行。线程的优先级用数字表示,范围从1到10,一个线程的缺省优先级是5。

Java 的线程优先级调度会委托给操作系统去处理,所以与具体的操作系统优先级有关,如非特别需要,一般无需设置线程优先级。

注意

线程的优先级,不是说哪个线程优先执行,如果设置某个线程的优先级高。那就是有可能被执行的概率高。并不是优先执行。

2 优先级的使用

(线程启动之前设置)

(两个方法------>

获得优先级------>int getPriority()

设置优先级------>void setPriority(int newPriority)

(Thread.MAX_PRIORITY------>10, Thread.MIN_PRIORITY------>1)

使用下列方法获得或设置线程对象的优先级。

  • int getPriority();
  • void setPriority(int newPriority);

**注意:**优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高的线程后调用优先级低的线程。

java 复制代码
class Priority implements Runnable{
    
  private int num = 0;				//计数
  private boolean flag = true;		//生死牌
  @Override
  public void run() {
    while(this.flag){
      System.out.println(Thread.currentThread().getName()+" "+num++);
     }
   }
  //stop方法  
  public void stop(){
    this.flag = false;
   }
}



public class PriorityThread {
  public static void main(String[] args)throws Exception {
      
    Priority p1 = new Priority();
    Priority p2 = new Priority();
    Thread t1 = new Thread(p1,"线程1");
    Thread t2 = new Thread(p2,"线程2");

    System.out.println(t1.getPriority());	//5, 默认线程优先级
      
    //Thread.MAX_PRIORITY = 10
    t1.setPriority(Thread.MAX_PRIORITY);
    //Thread.MAX_PRIORITY = 1
    t2.setPriority(Thread.MIN_PRIORITY);
    t1.start();
    t2.start();
    Thread.sleep(1000);
    p1.stop();
    p2.stop();
   }
}

三 (守护线程)

1 什么是守护线程

(Daemon Thread(守护线程)------>服务线程------>为User Thread(用户线程)做服务的)

(用户线程------>只有两种情况会死掉------>在run中异常终止------>正常把run执行完毕,线程死亡)

(守护线程------>随着用户线程的死亡而死亡)

在Java中有两类线程:

  • User Thread(用户线程):就是应用程序里的自定义线程。
  • Daemon Thread(守护线程):比如垃圾回收线程,就是最典型的守护线程。

守护线程(即Daemon Thread),是一个服务线程,准确地来说就是服务其他的线程,这是它的作用,而其他的线程只有一种,那就是用户线程。

守护线程特点:

  • 守护线程会随着用户线程死亡而死亡。

守护线程与用户线程的区别:

  • 用户线程,不随着主线程的死亡而死亡。用户线程只有两种情况会死掉,1在run中异常终止。2正常把run执行完毕,线程死亡。

  • 守护线程,随着用户线程的死亡而死亡,当用户线程死亡守护线程也会随之死亡。

2 守护线程的使用

(Jvm主线程在运行时,会启动很多的辅助线程,其中有一个辅助线程就是专门做垃圾回收的辅助线程,如果主线程结束,那么这个辅助线程会随之消亡的)

(setDaemon(true)方法------>设置为守护线程------>可以守护任何线程包括子线程)

java 复制代码
//守护线程
class Daemon implements Runnable{

  @Override
  public void run() {
    for(int i=0;i<20;i++){
      System.out.println(Thread.currentThread().getName()+" "+i);
      try {
        Thread.sleep(2000);
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
   }
}


//子线程
class UsersThread implements Runnable{

  @Override
  public void run() {
      
      
    Thread t = new Thread(new Daemon(),"Daemon");
    //将该线程设置为守护线程
    t.setDaemon(true);
    t.start();
      
      
    for(int i=0;i<5;i++){
      System.out.println(Thread.currentThread().getName()+" "+i);
      try {
        Thread.sleep(500);
       } catch (InterruptedException e) {
        e.printStackTrace();
       }
     }
   }
}

//主线程
public class DaemonThread {
  public static void main(String[] args)throws Exception {
    Thread t = new Thread(new UsersThread(),"UsersThread");
    t.start();
    Thread.sleep(1000);
    System.out.println("主线程结束");
   }
}
相关推荐
爱敲代码的小冰28 分钟前
spring boot 过滤器
java·spring boot·后端
大梦百万秋29 分钟前
Go 语言新手入门:快速掌握 Go 基础
开发语言·后端·golang
CircleMouse37 分钟前
IDEA 2024 版本配置热部署
java·ide·intellij-idea
轩情吖39 分钟前
C++STL之list(用法超详解)
开发语言·数据结构·c++·链表·迭代器·list·带头双向循环链表
huaqianzkh1 小时前
桥接模式的理解和实践
java·设计模式·桥接模式
keep.ac1 小时前
ArrayList源码分析、扩容机制面试题,数组和List的相互转换,ArrayList与LinkedList的区别
java·开发语言
夕泠爱吃糖1 小时前
C++中如何实现接口继承与实现继承,以及它们的区别?
开发语言·c++
西岭千秋雪_1 小时前
设计模式の装饰者&组合&外观模式
java·python·设计模式·组合模式·装饰器模式·外观模式
AI人H哥会Java2 小时前
【JAVA】Java项目实战—分布式微服务项目:分布式文件存储系统
java
程序媛徐师姐2 小时前
Java基于SpringBoot的飘香水果购物网站,附源码
java·spring boot·飘香水果购物网站·java飘香水果购物网站·飘香水果·水果购物网站