Java—多线程的实现及常用操作sleep、yield、join、线程停止方法详解

1.进程与线程

进程:指OS中一个程序的执行周期

线程:一个程序同时执行多个任务,其中每个任务都是一个线程

二者区别:每个进程拥有自己的一整套变量,线程则共享数据。共享变量是的线程之间通信比进程间的更有效、方便

2.线程的五种状态:

创建–>就绪–>阻塞–>运行–>终止

在这里插入图片描述

3.Java多线程

3.1通过继承Thread类覆写run()方法来实现多线程

启动多线程:public synchronized void start() ,用此方法来自动调用线程的run()方法

线程的核心类包: java.lang.Thread

public class MyThread extends Thread{ //线程主体类

    private String line;

    public MyThread (String line){
        this.line = line;
    }

    @Override
    public void run(){  //覆写run()方法,线程从此开始执行
        for(int i = 0; i<10; i++){
            System.out.println(this.line+",i="+i);
        }
    }

    public static void main(String[] args) {

        MyThread myThread1 = new MyThread("thread1") ;
        MyThread myThread2 = new MyThread("thread2") ;
        MyThread myThread3 = new MyThread("thread3") ;

        //顺序执行,非多线程
        myThread1.run();
        myThread2.run();
        myThread3.run();

        //通过启动多线程:public synchronized void start()此方法来自动调用线程的run()方法
        myThread1.start();
        myThread2.start();
        myThread3.start();//此时三个线程是同时、交替执行的
    }
}

3.2通过实现Runnable()接口

Thread类的构造方法:public Thread(Runnable target) ,用来接收Runnable接口对象

//Runnable()接口
@FunctionalInterface
public interface Runnable {
    /**
    * When an object implementing interface <code>Runnable</code> is used
    * to create a thread, starting the thread causes the object's
    * <code>run</code> method to be called in that separately executing
    * thread.
    * <p>
    * The general contract of the method <code>run</code> is that it may
    * take any action whatsoever.
    *
    * @see java.lang.Thread#run()
    */
    
    public abstract void run();
}
//利用Runnable接口实现多线程
class MyThread implements Runnable{

        private String line;
        public MyThread(String line){
            this.line = line;
        }

        @Override
        public void run(){
            for(int i = 0; i<10 ;i++){
                System.out.println(this.line+",i="+i);
            }
        }
}

public class TestDemo {
    public static void main(String[] args) {
        MyThread myThread1= new MyThread("Thread1");
        MyThread myThread2= new MyThread("Thread2");
        MyThread myThread3= new MyThread("Thread3");

        new Thread(myThread1).start();
        new Thread(myThread2).start();
        new Thread(myThread3).start();
    }
}

//利用Runnable接口可实现共享,实现一个简单的卖票流程
class MyThread1 implements Runnable{
    private int tickets = 5;

    public void setTickets(int tickets){
        this.tickets = tickets;
    }

    @Override
    public void run(){
        while(tickets>0){
            System.out.println(Thread.currentThread().getName()+"剩余票数:"+tickets--);
        }
    }
}

public class SellTickets {
    public static void main(String[] args) {

        MyThread1 myThread1 = new MyThread1();
        MyThread1 myThread2 = new MyThread1();
        MyThread1 myThread3 = new MyThread1();
        new Thread(myThread1).start();
        new Thread(myThread2).start();
        new Thread(myThread3).start();

    }
}

Runnable接口实现多线程效果图:
在这里插入图片描述
卖票窗口效果图:
在这里插入图片描述

  • 通常采用匿名内部类Lambda表达式来创建Runnable对象
//使用匿名内部类创建Runnable对象
public class Thread1 {
    public static void main(String[] args) {
        //Runnable runnable = new Runnable();
        //Thread thread = new Thread(runnable,"子线程1");
        //thread.start();----->new Thread (thread).start();
        new Thread(new Runnable(){
            @Override
            public void run(){
                System.out.println("HelloWorld");
            }
        }).start();  //注意!!!
    }
}

//使用Lamdba表达式创建Runnable对象
public class Thread1 {
    public static void main(String[] args) {
        Runnable runnable = () -> System.out.println("HelloWorld");
        new Thread(runnable).start();
    }
}

3.3Thread类和Runnable区别

Thread:类,单继承,是Runnable接口的子类,覆写了Runnable接口的run()方法

Runnable:接口,可被多个类调用

4.Callable实现多线程

Runnable中的run()方法无返回值,用Callnable来实现带有返回值的多线程

Callable接口存在于java.util.concurrent包中

//Callable接口
@FunctionalInterface
public interface Callable<V> {
    /**
    * Computes a result, or throws an exception if unable to do so.
    *
    * @return computed result
    * @throws Exception if unable to compute a result
    */
    
    V call() throws Exception;
}
//使用Callable来定义卖票窗口
class MyThread2 implements Callable<String> {

    private int tickets = 10;

    public void setTickets(int tickets){
        this.tickets = tickets;
    }

    @Override
    public String call() throws Exception {
        while(this.tickets>0){
            System.out.println(Thread.currentThread().getName()+"剩余票数:"+this.tickets--);
        }
        return "票卖完啦!欢迎下次光临";
    }
}

Callable实现卖票窗口效果图:
在这里插入图片描述

  • 无论何种情况,启动线程只能调用Thread类中的start()方法。观察下面Callable继承树:在这里插入图片描述
    注意Thread和FutureTask两大板块!
//启动并获得多线程的执行结果
public class SellTickets {
    public static void main(String[] args)throws ExecutionException, InterruptedException {
    
        //1.实例化多线程对象
        MyThread2 myThread1 = new MyThread2();
        MyThread2 myThread2 = new MyThread2();
        MyThread2 myThread3 = new MyThread2();

        //注意!!2.通过FutureTask类来包装Callable对象
        FutureTask<String> task1 = new FutureTask<>(new MyThread2());
        FutureTask<String> task2 = new FutureTask<>(new MyThread2());
        FutureTask<String> task3 = new FutureTask<>(new MyThread2());
        
        //3.启动线程
        new Thread(task1).start();
        new Thread(task2).start();
        new Thread(task3).start();

        //4.获取多线程的执行结果
        System.out.println(task1.get());
        
        /*在调用task.get()方法时抛出异常ExecutionException, InterruptedException*/
    }
}

注意

  1. Future代表一个异步的计算,可以从中得到计算结果,查看计算状态,它实现FutureTask可以被提交给Executor执行,多个线程能从中得到计算结果
  2. Callable和Future要配合使用,Future给出的get结果有两种状态:当结果还未被计算出来,线程被挂起,FutureTak内部使用一个单链表维持等待的线程;当结果已被计算出来,解除线程挂起,得到计算结果
  3. Future接口:代表一个异步的计算结果,接口中的方法用来检查计算是否完成、等待完成以及得到计算结果。其中get()方法得到结果
  4. FutureTask类:提供异步计算的结果的任务。它实现了RunnableFuture接口,而RunnableFuture接口又继承了Runnable和Future接口。所以FutureTask可以用来包装Callable或者Runnbale对象

4.多线程的常用操作方法

1.线程的命名和获取

//命名线程
MyThread myThread = new MyThread();
new Thread(myThread,"子线程A").start();

//获取线程名
Thread.currentThread().getName();

主方法本身就是一个线程,所有的线程都是通过主线程创建并启动的

2.线程休眠sleep()

线程休眠:指是让线程暂缓执行一下,等到了预计时间之后再恢复执行

线程休眠时会交出CPU,让CPU去执行其他任务,但是sleep不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象

调用sleep()会抛出异常:InterruptedException

线程休眠时间以毫秒作为单位

//处理线程休眠操作
class MyThread3 implements Runnable{
    private boolean flag = true;

    public void setFlag(boolean flag){
        this.flag = flag;
    }

    @Override
    public void run(){
        int i = 0;
        while(this.flag){
            System.out.println("线程"+Thread.currentThread().getName()+"当前执行次数i:"+ ++i +"次");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


public class SleepThread {

    public static void main(String[] args) {

        MyThread3 mt1 = new MyThread3();
        MyThread3 mt2 = new MyThread3();
        MyThread3 mt3 = new MyThread3();

        new Thread(mt1,"子线程A").start();
        new Thread(mt2,"子线程B").start();
        new Thread(mt3,"子线程C").start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
/*
注意!此处三个线程并非同时休眠,所有代码一次进入run()方法中,真正进入到方法的对象可能是多个,也可能是一个。进入代码的顺序可能有差异,但是总体的执行是并发执行。
*/

效果图:
在这里插入图片描述
3.线程让步yield()

线程让步是指暂停当前正在执行的线程对象,并执行其他线程

yield()与sleep()相似,但yield不能控制交出CPU的具体时间且只能让拥有相同优先级的线程有获取CPU执行时间的机会

调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间

//yield()下的线程休眠:休眠时间不确定,同一优先级获取CPU执行机会
class MyThread4 implements Runnable{

    @Override
    public void run(){
        for(int i = 0 ;i<5; i++){

            Thread.yield();
            System.out.println(Thread.currentThread().getName()+"循环执行第"+ ++i +"次");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


public class YieldThread {

    public static void main(String[] args) {

        MyThread4 mt1 = new MyThread4();
        MyThread4 mt2 = new MyThread4();
        MyThread4 mt3 = new MyThread4();

        new Thread(mt1,"子线程A").start();
        new Thread(mt2,"子线程B").start();
        new Thread(mt3,"子线程C").start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

效果图:
在这里插入图片描述
4.join()方法

join():等待该线程终止。如果在主线程中调用就会让主线程休眠,让调用该方法的线程run方法先执行后再开始执行主线程。

//join()在线程中的使用方法--等待该线程停止
class MyThread5 implements Runnable{
    @Override
    public void run(){
        try {
            System.out.println("主线程睡眠前的时间:");
            JoinThread.printTime();
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getName());
            System.out.println("睡眠结束的时间:");
            JoinThread.printTime();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

public class JoinThread {

    public static void main(String[] args) throws InterruptedException {
        MyThread5 mt1 = new MyThread5();
        Thread thread = new Thread(mt1,"子线程A");
        thread.start();
        System.out.println(Thread.currentThread().getName());
        thread.join();
        System.out.println("代码结束");
    }

    public static void printTime(){
        Date date = new Date();
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd  HH:mm:ss");
        String time = format.format(date);
        System.out.println(time);
    }
}

效果图:
在这里插入图片描述
5.线程停止

多线程中有三种方式可以停止线程:

  1. 设置标记位,可以使线程正常退出
  2. 使用stop方法强制使线程退出,但是该方法不太安全所以已经被废弃了
  3. 使用Thread类中的一个 interrupt() 可以中断线程
/**
*1.设置标记为使线程退出
**/
class MyThread6 implements Runnable {
    private boolean flag = true;

    @Override
    public void run() {
        int i = 1;
        while (flag) {
            try {
                Thread.sleep(2000);
                System.out.println("第" + i + "次执行,线程名称:" + Thread.currentThread().getName());
                i++;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}


public class StopThread {
    public static void main(String[] args) throws InterruptedException {
        MyThread6 mt1 = new MyThread6();
        Thread thread = new Thread(mt1,"子线程A");
        thread.start();
        Thread.sleep(1000);
        mt1.setFlag(false);
        System.out.println("线程结束");
    }
}

效果图:
在这里插入图片描述

/**
*2.调用stop()方法使线程退出
**/
        MyThread6 mt1 = new MyThread6();
        Thread thread = new Thread(mt1,"子线程A");
        thread.start();
        Thread.sleep(1000);
        thread.stop();
        System.out.println("线程结束");
        
/*为什么stop()不安全?
因为stop()方法会解除由线程获取的所有锁定,当调用此方法时,线程对象会立即停止正在执行的方法,若此方法为某种同步方法,如synchronized void { x = 3; y = 4;} ,线程会由于突然停止而产生不完整的残废数据
*/
/**
*3.使用Thread.interrupt()使线程退出
**/
class MyThread7 implements Runnable{

    private boolean flag = true;

    @Override
    public void run(){
        int i = 1;
        while(flag){
            try {
                Thread.sleep(1000);

                boolean bool = Thread.currentThread().isInterrupted();//判断当前线程中断情况
                if(bool){
                    System.out.println("线程处于非阻塞状态"+bool);//非阻塞状态
                    break;
                }
                System.out.println("第"+i+"次执行,线程名称:"+Thread.currentThread().getName());
                /*阻塞状态,线程被调用了interrupt()方法,清除中断标志后抛出异常java.lang.InterruptedException*/
                i++;
            } catch (InterruptedException e) {

                e.printStackTrace();
                System.out.println("退出阻塞");//退出阻塞状态,且中断标志被系统自动清除,并将bool重新设置为false
                boolean bool = Thread.currentThread().isInterrupted();
                System.out.println("阻塞中断:" + bool);
                return;//退出run()方法,中断进程
            }
        }
    }

    public void setFlag(boolean flag){
        this.flag = flag;
    }
}


public class InterruptThread {
    public static void main(String[] args) throws InterruptedException {
        MyThread7 mt1 = new MyThread7();
        Thread thread = new Thread(mt1,"子线程A");
        thread.start();
        Thread.sleep(3000);
        thread.interrupt();
        System.out.println("线程结束");
    }
}

效果图:
在这里插入图片描述

  • interrupte()方法并不会立即执行中断操作,而是给线程设置一个为true的中断标志(boolean型),根据线程状态进行不同操作
  • 如果线程状态为非阻塞,则仅仅是将线程的中断标志设为true
  • 阻塞状态:将中断标志设为true,如果是wait、sleep以及join三个方法引起的阻塞,则将线程的中断标志重新设置为false,并抛出InterruptedException;

完整代码移步:https://github.com/Loinbo

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值