Java笔记之线程(二十四)

一、线程基本概念

程序

其实就是我们写的代码

进程

进程是指正在运行的程序 如:电脑运行的QQ,就是一个进程

线程

线程由进程创建的,是进程的一个实际运作单位;一个进程可以拥有多个线程

单线程

同一时刻,只允许执行一个线程

多线程

同一个时刻,可以执行多个线程 如:QQ可以打开多个聊天窗口

并发

同一时刻,多个任务交替执行,有一种“貌似同时”的感觉,简单说就是单核cpu实现的多任务

并行

同一时刻,多个任务同时执行,多核cpu可以实现并行

二、线程基本使用☆

1.创建线程

线程结构图
在这里插入图片描述
线程创建

  • 方式一:继承Thread类,重写run方法
  • 方式二:实现Runnable接口,重写run方法

2.快速入门

继承Thread类
要求:编写一个程序,开启一个线程,每隔一秒,输出“JAVA”,当输出3次后结束该线程

public class test {
    public static void main(String[] args) throws InterruptedException {
        test1 test1 = new test1();
        test1.start();      //启动线程 -> 执行test1的run方法
        for (int i = 0; i < 3; i++) {
            System.out.println("主线程 "+Thread.currentThread().getName());    //主线程和子线程是交替执行..不会出现阻塞现象
            Thread.sleep(1000);
        }
    }
}

class test1 extends Thread{
    int sum = 0;
    //该run方法并不是Thread类中的,而是Thread实现的Runnable接口中的run方法
    @Override
    public void run() {
        while (true){
            sum++;
            System.out.println("java 子线程 "+Thread.currentThread().getName());

            try {
                sleep(1000);    //睡眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (sum==3){
                break;
            }
        }
    }
}

实现Runnable接口
由于java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时如果需要继承Thread类显然是不可能的,所以可以通过实现Runnable接口

public class test {
    public static void main(String[] args) throws InterruptedException {
        test1 test1 = new test1();
        Thread thread = new Thread(test1);
        thread.start();
    }
}

class test1 implements Runnable{
    int sum = 0;
    //该run方法并不是Thread类中的,而是Thread实现的Runnable接口中的run方法
    @Override
    public void run() {
        while (true){
            sum++;
            System.out.println("java 子线程 "+Thread.currentThread().getName());

            try {
                Thread.sleep(1000);    //睡眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (sum==3){
                break;
            }
        }
    }
}

应用案例-多线程执行
要求:创建两个线程,一个线程每隔一秒输出 “java” 输出10次退出,另一个线程每隔一秒输出 “scala” 输出5次退出

public class test {
    public static void main(String[] args) throws InterruptedException {
        test1 test1 = new test1();
        test2 test2 = new test2();
        Thread thread = new Thread(test1);
        Thread thread1 = new Thread(test2);
        thread.start();
        thread1.start();
    }
}

class test1 implements Runnable{
    int sum = 0;
    @Override
    public void run() {
        while (true){
            sum++;
            System.out.println("java "+Thread.currentThread().getName());

            try {
                Thread.sleep(1000);    //睡眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (sum==10){
                break;
            }
        }
    }
}

class test2 implements Runnable{
    int sum = 0;
    @Override
    public void run() {
        while (true){
            sum++;
            System.out.println("scala "+Thread.currentThread().getName());

            try {
                Thread.sleep(1000);    //睡眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (sum==5){
                break;
            }
        }
    }
}

Thread类和Runnable接口的区别
1.Thread类实际是实现了Runnable接口
2.实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制

三、线程的同步

1.问题引入

使用多个线程模拟三个售票窗口,对100张票进行出售

Thread方式

public class test {
    public static void main(String[] args) throws InterruptedException {
        test1 test1 = new test1();
        test1 test2 = new test1();
        test1 test3 = new test1();
        test1.start();
        test2.start();
        test3.start();
    }
}

class test1 extends Thread{
    private static int sum = 100;
    @Override
    public void run() {
        while (true){

            System.out.println("窗口 "+Thread.currentThread().getName()+" 售出一张票 剩余票数为"+ --sum);

            try {
                Thread.sleep(50);    //睡眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (sum<=0){
                System.out.println("售票结束");
                break;
            }
        }
    }
}

Runnable接口

public class test {
    public static void main(String[] args) throws InterruptedException {
        test1 test1 = new test1();
        new Thread(test1).start();
        new Thread(test1).start();
        new Thread(test1).start();
    }
}

class test1 implements Runnable{
    private static int sum = 100;
    @Override
    public void run() {
        while (true){

            System.out.println("窗口 "+Thread.currentThread().getName()+" 售出一张票 剩余票数为"+ --sum);

            try {
                Thread.sleep(50);    //睡眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (sum<=0){
                System.out.println("售票结束");
                break;
            }
        }
    }
}

运行结果如下,可以看出当售票结束以后,其他窗口仍在售票,这里就出现了线程同步的问题
在这里插入图片描述

2.线程终止

当线程完成任务后,会自动退出
还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式

public class test {
    public static void main(String[] args) throws InterruptedException {
        test1 test1 = new test1();
        test1.start();
        //让主线程休眠5秒
        Thread.sleep(5*1000);
        test1.setLoop(false);
    }
}

class test1 extends Thread{
    private int sum =0;
    //设置一个控制变量
    private boolean loop =true;
    @Override
    public void run() {
        while (loop){
            try {
                Thread.sleep(50);    //睡眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"..."+ ++sum);
        }
    }

    public void setLoop(boolean loop) {
        this.loop = loop;
    }
}

四、线程的常用方法

1.第一组常用方法

方法名功能
setName设置线程名称
getName返回该线程的名称
start使该线程开始执行
run调用线程对象
setPriority更改线程的优先级
getPriority获取线程的优先级
sleep在指定的毫秒数内让当前正在执行的线程休眠
interrupt中断线程(注意:是中断而不是中止,相当于循环结构中continue)
public class test {
    public static void main(String[] args) throws InterruptedException {
        test1 test1 = new test1();
        test1.start();
        test1.setName("张三");

        //主线程执行五秒后,执行test1.interrupt();语句
        //由于主线程和test1线程同时进行,此时test1线程正在休眠执行test1.interrupt();以后将被唤醒
        for (int i = 0; i < 5 ; i++) {
            Thread.sleep(1000);
            System.out.println("主线程..."+i);
        }
        test1.interrupt();  //中断test1线程的休眠
    }
}

class test1 extends Thread{
    @Override
    public void run() {
        while (true){
            //输出0到99
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName()+"..."+i);
            }
            try {
                Thread.sleep(20000);    //休眠20秒
            } catch (InterruptedException e) {
                //当该线程执行到一个interrupt时,就会catch一个异常,可以加入自己的业务代码
                System.out.println(Thread.currentThread().getName()+"被interrupt中断休眠了");
            }
        }
    }
}

在这里插入图片描述

2.第二组常用方法

方法名功能
yield线程的礼让。让出cpu,让其它线程执行,但礼让的时间不确定,所以也不一定礼让成功
join线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务

join案例:子线程和主线程同时运行,当主线程输出五次以后让子线程运行,子线程运行完以后再执行子线程

public class test {
    public static void main(String[] args) throws InterruptedException {
        test1 test1 = new test1();
        test1.start();

        for (int i = 0; i < 20; i++) {
            Thread.sleep(1000);    //休眠1秒
            System.out.println("主线程..."+i);     //此时子线程和同时运行
            if (i==5){
                System.out.println("子线程先执行完,然后主线程再执行");
                test1.join();
            }
        }
    }
}

class test1 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"子线程..."+i);
            try {
                Thread.sleep(1000);    //休眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

3.用户线程和守护线程☆

用户线程:也叫工作线程,当线程的任务完或通知方式结束
守护线程:一般为工作线程服务的,当所有的用户线程结束,守护线程自动结束(常见的守护线程:垃圾回收机制)

public class test {
    public static void main(String[] args) throws InterruptedException {
        test1 test1 = new test1();
        test1.setDaemon(true);  //将子线程设置为守护线程,当主线程结束后子线程自动结束,如果不设置守护线程则子线程将一直无限循环
        test1.start();
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程...");
            Thread.sleep(1000);
        }
    }
}

class test1 extends Thread{
    @Override
    public void run() {
        for (;;) {  //无限循环
            System.out.println(Thread.currentThread().getName()+"子线程...");
            try {
                Thread.sleep(1000);    //休眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

4.注意事项

  1. start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新的线程
  2. 线程优先级的范围
  3. interrupt,中断线程,但并没有真正的结束线程,所以一般用于中断正在休眠线程
  4. sleep线程的静态方法,使当前线程休眠

五、线程的生命周期

在这里插入图片描述

其中RUNNABLE:可以分为就绪状态和运行状态。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王博1999

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值