线程简介1-多任务,多线程以及线程创建和简单使用=>继承Thread,实现Runnable,实现Callable,多线程访问单对象

线程

多任务

  • 多任务能够充分的利用CPU的资源,提高程序的运行效率
  • 多任务的本质是同一时间执行多个任务

多任务的执行方式:并发,并行

  • 并发:指多个任务的在一段时间内交替的执行,但是交替时间很短,形成一种同时执行的错觉。
  • 并行:真正的同时运行,只能发生在多核多CPU的情况下,一个CPU执行一个任务,多个任务同一时间,同时执行。

多线程

  • 指多个线程并发执行的过程,线程根据不同的顺序进行同时的执行的过程。
  • 充分利用资源,提高运行效率

普通方法调用和多线程方法调用

  • 普通方法调用:
    在这里插入图片描述
  • 多线程方法调用:
    **3和4两个线程并行的执行
    **
    在这里插入图片描述

线程(thread),进程(process),程序

  • 程序:程序是指令和数据的有序集合,是一个静态的概念。
  • 进程:进程是程序的一次执行过程,是一个动态的,是一个资源分配单位
  • 一个进程至少包含一个或多个线程。
  • 线程:线程是CPU调度和执行的单位。

核心概念关于线程

  • 线程就是独立执行的路径。
  • 程序运行时,就算没有创建线程,程序后台本身也会有线程,如主线程(main),回收线程(gc)。
  • main是主线程,被用于程序的执行
  • 一个进程中如果有多个线程,那么线程的运行由调度器决定,先后顺序不能改变
  • 对同一份资源进行操作时,多线程可能会导致资源的抢夺问题,这时候需要考虑程度的并发
  • 线程的使用会带来额外的开销,如调度时间,并发控制的开销
  • 每个线程都会在自己的内存内交互,内存控制不当会导致数据的不统一。

线程的创建

继承thread类

  1. 自定义的线程类继承thread类
  2. 重写run()方法
  3. 创建线程对象
  4. 在主线程中执行start()方法
    在主线程中执行start()方法的话自定义线程和主线程就是交替的执行,若执行run方法就是在主线程中按顺序执行
  • 代码示例:
package com.XianCheng.Thread;

/**
 * 自定义线程类的实现
 *      方法一:继承Thread类实现
 *          1.继承Thread类
 *          2.重写run()方法
 *          3.创建线程对象
 *          4.执行start()方法
 */
public class ThreadTest  extends Thread{
    //创建自定义线程
    //重写run()方法
    @Override
    public void run() {
        for (int i = 0; i < 23; i++) {
            System.out.println("我是被重写的run方法");
        }
    }
    //创建主线程
    public static void main(String[] args) {
        //创建线程对象
        ThreadTest td1 = new ThreadTest();
        //调用start方法
        td1.start();
        for (int i = 0; i < 2000; i++) {
            System.out.println("我是主线程");
        }
    }
}

线程开启后不一定马上运行,线程的运行由CPU决定

实现Runnable接口创建线程

  • 方法二:通过继承Runnable接口实现线程的创建
    1. 实现Runnable接口
    2. 重写run方法
    3. 创建实现的Runnable接口的对象
    4. 创建线程对象:new thread()
    5. 将实现类的对象交由创建的线程对象
  • 代码案例:
package com.XianCheng.Runnable;

/**
 * 方法二:通过继承Runnable接口实现线程的创建
 *      1.实现Runnable接口
 *      2.重写run方法
 *      3.创建实现的Runnable接口的对象
 *      4.创建线程对象:new thread()
 *      5.将实现类的对象交由创建的线程对象
 */
public class RunnalbleTest implements Runnable{
    //重写run方法

    @Override
    public void run() {
        //创建方法证明线程创建成功
        for (int i = 0; i < 50; i++) {
            System.out.println("Runnable接口实现创建线程成功"+i);
        }
    }

    public static void main(String[] args) {
        //创建Runnable接口实现类对象
        RunnalbleTest runnalbleTest = new RunnalbleTest();
        //将实现类的对象传递给thread的对象实现
        //创建线程对象
        Thread thread = new Thread(runnalbleTest);
        //调用start方法运行线程
        thread.start();
        /**
         *         Thread thread = new Thread(runnalbleTest);
         *         //调用start方法运行线程
         *         thread.start();
         *         也可以直接写成
         *              new Thread(runnalbleTest).start();
         */
        //主线程对比
        for (int i = 0; i < 1000; i++) {
            System.out.println("w我是主线程"+i);
        }
    }

}

注意Thread类的继承由于java的单继承性,具有一定的局限性,Runnable接口的实现可以避免单继承的局限性,更加灵活方便,可以使得同一个对象被多个线程使用

实现Callable接口创建线程

  1. 继承Callable接口
  2. 重写call方法
  3. 创建服务对象
  4. 创建服务,创建线程池:ExecutorService ser= Executors.newFixedThreadPool(3);
  5. 提交执行: Future r1=ser.submit(testCallable);
  6. 获取结果:boolean s1=r1.get();
  7. 关闭服务:ser.shutdown();
package com.XianCheng.Callable;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

//实现callable接口创建线程
/*
   方法三:实现callable接口创建线程
        1、 实现callable接口==>Callable的返回值和重写的call方法的返回值相同
        2、 重写call方法
        3、 创建服务目标对象
        4、 创建执行服务: ExecutorService ser= Executors.newFixedThreadPool(3);
        5、 提交执行: Future <Boolean> r1=ser.submit(testCallable);
        6、 获取结果:boolean s1=r1.get();
        7、 关闭服务:ser.shutdown();
 */
public class TestCallable implements Callable<Boolean> {
    //创建对应属性
    private String url;
    private  String name;

    public TestCallable() {
    }

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

    @Override
    public Boolean call() throws Exception {
        //调用下载器
        down down = new down();
        down.downway(url,name);
        System.out.println(Thread.currentThread().getName()+"下载完成");
        return true;
    }

    //主线程中调用
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建服务对象
        TestCallable testCallable = new TestCallable("https://c-ssl.duitang.com/uploads/blog/202302/18/20230218223919_23cf8.jpeg","4.jpg");
        TestCallable testCallable1 = new TestCallable("https://c-ssl.duitang.com/uploads/blog/202302/18/20230218223919_23cf8.jpeg","3.jpg");
        TestCallable testCallable2 = new TestCallable("https://c-ssl.duitang.com/uploads/blog/202302/18/20230218223919_23cf8.jpeg","2.jpg");
        //创建执行服务,创建线程池并规定大小
        ExecutorService ser= Executors.newFixedThreadPool(3);
        //提交执行
        Future <Boolean> r1=ser.submit(testCallable);
        Future <Boolean> r2=ser.submit(testCallable1);
        Future <Boolean> r3=ser.submit(testCallable2);
        //获取结果
        boolean s1=r1.get();
        boolean s2=r2.get();
        boolean s3=r3.get();
        //输出结果
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
        //关闭服务
        ser.shutdown();

    }
}
//下载器
class down{
    //下载方法
    public void downway(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


多线程的图片下载案例
  • 使用多线程对网络图片进行下载,可用于证明多线程中线程是并行执行的。
  • 使用commons-io-jar包中的方法将网络url转为图片保存到本地
package com.XianCheng.Thread;
//使用多线程对网络图片进行下载

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
 * 1.导入commons-io-jar包
 * 2.使用commons-io-jar包中的FileUtils下的copyFileToFile()方法
 * 3.给该方法传递一个url和保存的名字name
 * 4.构造器创建属性
 * 5.创建线程对象
 */
public class ThreadPhoto extends Thread{
    //创建属性
    private String url;
    private String name;
    //创建无参构造
    public ThreadPhoto() {
    }
    //创建有参构造
    public ThreadPhoto(String url, String name) {
        this.url = url;
        this.name = name;
    }
    //重写run方法

    @Override
    public void run() {
        //编写线程体
        //创建下载方法的对象
        WebDown webDown = new WebDown();
        webDown.WebDownLoad(url,name);
        System.out.println("下载"+name+"成功");
    }
    //主线程中调用对应的线程体
    public static void main(String[] args) {
        ThreadPhoto threadPhoto = new ThreadPhoto("https://c-ssl.duitang.com/uploads/item/202002/18/20200218155725_arms8.jpeg","1.jpg");
        ThreadPhoto threadPhoto1 = new ThreadPhoto("https://c-ssl.duitang.com/uploads/item/201801/13/20180113162938_AUPm2.jpeg","2.jpg");
        ThreadPhoto threadPhoto2 = new ThreadPhoto("https://c-ssl.duitang.com/uploads/blog/202102/10/20210210111704_cd68b.jpg","3.jpg");
        threadPhoto.start();
        threadPhoto1.start();
        threadPhoto2.start();

    }
}
//下载器
class WebDown{
    //下载方法
    public void WebDownLoad(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("下载器出现异常");
        }
    }
}

多线程同时访问一个对象
  • 多线程同时访问一个对象时,没有对对象继续并发操作时,可能会导致数据紊乱,导致数据的不安全。

  • 使用龟兔赛跑模拟多线程同时访问一个对象

    1. 将龟和兔分别当做一个线程,将比赛的总距离当做对象
    2. 两个线程同时访问该对象,并判断胜者
    3. 通过Thread.sleep()对兔这个线程进行休眠操作,达到龟兔赛跑中兔子偷懒的效果。
  • 代码展示:

package com.XianCheng.Test;
//模拟多个线程同时访问一个对象
//模拟学生,老师,黄牛三个线程,访问票这一个对象
public class Threadsoop implements Runnable{
    private int tick=10;

    //重写run方法
    @Override
    public void run() {
        while (true){
            if (tick<=0){
                break;
            }
            try {
                Thread.sleep(200);//在操作前会休眠多少毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
//            Thread.currentThread().getName()获取线程名
            System.out.println(Thread.currentThread().getName()+"--》拿到了第"+tick--+"张票");
        }
    }

    public static void main(String[] args) {
        //创建线程对象
        Threadsoop threadsoop = new Threadsoop();
        //多线程使用单对象
        new Thread(threadsoop,"小明").start();
        new Thread(threadsoop,"老师").start();
        new Thread(threadsoop,"黄牛").start();

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值