Java多线程知识梳理

Java-多线程

CPU

CPU中文名中央处理器,是电脑中进行逻辑运算时用的,主要由运算器,控制器,寄存器三部分组成,运算器是起着运算的作用,控制器是负责发出CPU每条指令所需要的信息,寄存器是为了存储运算或者指令的一些临时文件。线程运行在CPU上。

  • CPU的两种工作状态
  1. 内核态:运行的程序是操作系统,可以操作计算机硬件

  2. 用户态:运行的程序是应用程序,不能操作计算机硬件

    应用程序的运行必然涉及到计算机硬件的操作,那就必须有用户态切换到内核态下才能实现,所以计算机工作时在频繁发生内核态与用户态的转换

线程和进程的区别

  • 说起进程就必须要说一下程序,程序是指令和数据的有序集合
  • 而进程就是程序执行的过程,它是一个动态概念,是系统资源分配的单位
  • 线程是存在于进程中,一个进程中有一个或者多个线程同时执行,线程是CPU调度和执行的单位

java中创建线程的几种方法

1.继承Thread类

//第一种创建线程的方法
//继承Thread类
public class TestThread01 extends Thread {

    /**
     * 继承Thread方法,因为Thread方法底层也是实现Runnable接口所以第一步都需要重写run方法
     */
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 50; i++) {
            System.out.println("执行run方法"+i);
        }
    }
    //主线程main方法
    public static void main(String[] args) {
      	//在主线程main方法中创建一个子线程TestThread01
        TestThread01 testThread01 = new TestThread01();
        //这句代码相当于只有主线程一条线程执行run方法
		// testThread01.run();
        //运行子线程,这里相当于用子线程执行run方法
        //所以就是主线程和子线程同时交替执行
        testThread01.start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("执行main方法"+i);
        }
    }
}

2.实现Runnable接口

//创建线程的第二种方法:实现Runnable接口
public class TestThread03 implements Runnable {
    //重写run方法
    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("执行了run方法"+i);
        }
    }

    public static void main(String[] args) {
        TestThread03 testThread03 = new TestThread03();
        Thread thread = new Thread(testThread03);
        thread.start();
        for (int i = 0; i < 1000; i++) {
            System.out.println("执行main方法"+i);
        }
    }
}

3.实现Callable接口

通过实现Callable接口使用多线程同时下载三张图片:

public class TestCallable implements Callable<String> {
    private String url;
    private String name;

    TestCallable(String url,String name){
        this.url=url;
        this.name=name;
    }

	//和上面两种方法不同的是Callable接口重写的是call方法
    @Override
    public String call() throws Exception {
        Downloads download = new Downloads();
        download.downloadImage(url,name);
        System.out.println("下载了文件名为"+name);
        return "true";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable testThread01 = new TestCallable("https://lbm-edu.oss-cn-shenzhen.aliyuncs.com/cover/4e4d3d86baa840999bb546e4d5c30a4cC语言实用教程.jpg","C语言.jpg");
        TestCallable testThread02 = new TestCallable("https://lbm-edu.oss-cn-shenzhen.aliyuncs.com/cover/5e7b616be66d4328bbde2e685714ccc4单片机.jpg","单片机.jpg");
        TestCallable testThread03 = new TestCallable("https://lbm-edu.oss-cn-shenzhen.aliyuncs.com/cover/c8d5a81fe4664f2583ca347d258884e8三国.jpg","三国.jpg");

        //创建线程池,设置线程池大小为3
        ExecutorService service = Executors.newFixedThreadPool(3);
        //分别将3个线程加入到线程池当中
        Future<String> submit1 = service.submit(testThread01);
        Future<String> submit2 = service.submit(testThread02);
        Future<String> submit3 = service.submit(testThread03);
        //拿到线程的运行结果
        submit1.get();
        submit2.get();
        submit3.get();
        //关闭线程池
        service.shutdown();

    }
}
class Downloads {
    //下载图片的方法
    public void downloadImage(String url,String name) {
        try {
            //这是工具类Apache下的Commons.io包,通过copyURLToFile方法可以下载图片
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            System.out.println("downloadImage出现了问题");
            e.printStackTrace();
        }
    }
}

运行结果:
在这里插入图片描述

可以看出运行的结果和线程执行的顺序不一样,说明是同步执行的,哪个运行速度快哪个先执行完。

什么是线程并发问题

//多个线程同时调用一个对象的时候出现并发问题
//买火车票
public class TestThread04 implements Runnable {
    private int ticketNums=10;

    @Override
    public void run() {
        while (true){
            if (ticketNums<=0){
                break;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票。。。。");
        }

    }

    public static void main(String[] args) {
        TestThread04 thread04 = new TestThread04();
        new Thread(thread04,"线程1").start();
        new Thread(thread04,"线程2").start();
        new Thread(thread04,"线程3").start();
        new Thread(thread04,"线程4").start();

    }
}

运行结果:
在这里插入图片描述

程序可以看出一共有四个程同时操作一个买票的对象,并且有同时拿到一张票的情况,这是因为还剩n张票的时候两个线程同时看见了,并且同时拿了。也有拿到-1的情况,那是因为还剩1张票的时候几个线程同时去拿,当某个线程拿完之后只剩下0张票了,但是另一个线程已经开始拿了,所以只能拿到-1了(可能说的不太清楚,表达能力也就到这了。。。。。)。

线程的五种状态

在这里插入图片描述

线程方法

1.线程停止–stop(不推荐使用)

  • 不推荐使用JDK提供的stop()、destroy()【以经废弃】方法。
  • 推荐线程自己运行完停止
  • 或者使用一个标志位进行终止,比如boolean flag=false的时候终止线程

2.线程休眠–sleep

  • sleep存在异常InterredException
  • sleep(时间)指定当前线程阻塞的毫秒数
  • sleep时间达到后线程进入就绪状态
  • 每个对象都有一个锁,sleep不会释放锁

3.线程礼让–yield

  • 线程礼让,让当前正在执行的线程暂停,但不是进入阻塞状态
  • 将线程从运行状态进入就绪状态
  • 意思就是让CPU重新调度,当时有可能再次调用礼让的那个线程,具体看CPU如何调度。

4.线程强制执行–join

  • Join合并线程,带此线程执行完成后再执行其他线程,其他线程阻塞
  • 可以想象成插队
public class TestJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("VIP来了"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();

        for (int i = 0; i < 1000; i++) {
            if (i == 200) {//当i=200的时候让线程插进来
                thread.join();
            }
            System.out.println("主线程执行中。。。。"+i);
        }
    }
}

运行结果:
在这里插入图片描述

可以看出当线程等于两百的时候“VIP”线程插队进来,其他线程进入阻塞状态,直到VIP线程运行完其他线程再开始运行

线程状态观测

线程可以处于以下六种状态之一:
在这里插入图片描述

public class TestState implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(10);//睡眠的时候状态为TIMED_WAITING(等待另一个线程执行动作,一直到达到指定等待时长-这里是5s)
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(".........");
    }

    public static void main(String[] args) {
        TestState testState = new TestState();
        Thread thread = new Thread(testState);

        Thread.State state = thread.getState();
        System.out.println(state);//刚new出来的时候状态为NEW
        thread.start();
        state=thread.getState();
        System.out.println(state);//线程开始的时候状态为RUNNABLE

        while (thread.getState()!= Thread.State.TERMINATED){//只要线程的状态不等于终止状态就一直观察并打印线程的状态
            state=thread.getState();
            System.out.println(state);
        }
    }
}

线程优先级

  • java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度那个线程来执行
  • 线程的优先级用数字表示,范围1~10
    • Thread.MIN_PRIORITY=1
    • Thread.MAX_PRIORITY=10
    • Thread.NORM_PRIORITY=5
  • 使用一下方法可以设置或者获得优先级
    • getPriority()
    • setPriority(int xxx)

守护(daemon)线程–setDaemon(true)

  • 线程分为用户线程守护线程
  • 比如说常见的main方法就是用户线程而GCC垃圾回收就是守护线程
  • 虚拟机不需要等待守护线程执行完毕
  • 虚拟机要等待用户线程执行完毕
  • 常见的守护线程:后台记录操作日志,监控内存,垃圾回收
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值