Java之多线程详解

本文深入探讨了Java中的进程与线程概念,强调线程作为CPU调度执行的基本单位。通过示例说明了三种创建线程的方式:继承Thread类、实现Runnable接口(推荐)以及实现Callable接口,并给出了多线程图片保存和抢票等实际应用示例。
摘要由CSDN通过智能技术生成
  • Process与Thread(进程和线程)

程序是指令和数据的集合,是一个静态的概念。

进程则是执行程序的一次过程,是动态的概念,是系统资源分配的单位。

通常一个进程中可以包含若干个线程,一个进程中至少有一个线程,不然没有存在的意义,线程是CPU调度和执行的单位。 

例如:一个视频是一个进程,那么其中有播放声音、显示画面、看弹幕等多个线程。(main()相当于主线程,其他的方法为其他的线程)

  • 创建线程

方式一:继承Thread类,重写run() ,调用start()开启线程

调用run()方法按顺序依次执行

package 多线程;
//继承Thread类,重写run,调用start开启线程
public class Create extends Thread{
    @Override
    public void run() {
        //run方法线程体
        for(int i = 0;i<20;++i){
            System.out.println("我在看代码--" + i);
        }
    }

    public static void main(String[] args) {
        //主方法main线程,主线程

        //创建一个线程对象
        Create create = new Create();

        //调用run方法按顺序执行
        create.run();
        for (int i = 0; i < 20; i++) {
            System.out.println("我在学习多线程--" + i);
        }
    }
}
/*
结果为:
D:\Java\jdk\bin\java.exe "-javaagent:D:\java\IntelliJ IDEA Educational Edition 2020.1.1\lib\idea_rt.jar=64891:D:\java\IntelliJ IDEA Educational Edition 2020.1.1\bin" -Dfile.encoding=UTF-8 -classpath D:\java\jdk\jre\lib\charsets.jar;D:\java\jdk\jre\lib\deploy.jar;D:\java\jdk\jre\lib\ext\access-bridge-64.jar;D:\java\jdk\jre\lib\ext\cldrdata.jar;D:\java\jdk\jre\lib\ext\dnsns.jar;D:\java\jdk\jre\lib\ext\jaccess.jar;D:\java\jdk\jre\lib\ext\jfxrt.jar;D:\java\jdk\jre\lib\ext\localedata.jar;D:\java\jdk\jre\lib\ext\nashorn.jar;D:\java\jdk\jre\lib\ext\sunec.jar;D:\java\jdk\jre\lib\ext\sunjce_provider.jar;D:\java\jdk\jre\lib\ext\sunmscapi.jar;D:\java\jdk\jre\lib\ext\sunpkcs11.jar;D:\java\jdk\jre\lib\ext\zipfs.jar;D:\java\jdk\jre\lib\javaws.jar;D:\java\jdk\jre\lib\jce.jar;D:\java\jdk\jre\lib\jfr.jar;D:\java\jdk\jre\lib\jfxswt.jar;D:\java\jdk\jre\lib\jsse.jar;D:\java\jdk\jre\lib\management-agent.jar;D:\java\jdk\jre\lib\plugin.jar;D:\java\jdk\jre\lib\resources.jar;D:\java\jdk\jre\lib\rt.jar;D:\JavaSE\基础语法\out\production\基础语法 多线程.Create
我在看代码--0
我在看代码--1
我在看代码--2
我在看代码--3
我在看代码--4
我在看代码--5
我在看代码--6
我在看代码--7
我在看代码--8
我在看代码--9
我在看代码--10
我在看代码--11
我在看代码--12
我在看代码--13
我在看代码--14
我在看代码--15
我在看代码--16
我在看代码--17
我在看代码--18
我在看代码--19
我在学习多线程--0
我在学习多线程--1
我在学习多线程--2
我在学习多线程--3
我在学习多线程--4
我在学习多线程--5
我在学习多线程--6
我在学习多线程--7
我在学习多线程--8
我在学习多线程--9
我在学习多线程--10
我在学习多线程--11
我在学习多线程--12
我在学习多线程--13
我在学习多线程--14
我在学习多线程--15
我在学习多线程--16
我在学习多线程--17
我在学习多线程--18
我在学习多线程--19

进程已结束,退出代码0

*/

调用start()方法穿插执行代码,并且每次运行的结果都不相同(穿插顺序不同)

package 多线程;
//继承Thread类,重写run,调用start开启线程
public class Create extends Thread{
    @Override
    public void run() {
        //run方法线程体
        for(int i = 0;i<20;++i){
            System.out.println("我在看代码--" + i);
        }
    }

    public static void main(String[] args) {
        //主方法main线程,主线程

        //创建一个线程对象
        Create create = new Create();

        //调用start()方法,开启线程
        create.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("我在学习多线程--" + i);
        }
    }
}
/*
结果为:
我在学习多线程--0
我在看代码--0
我在学习多线程--1
我在看代码--1
我在看代码--2
我在看代码--3
我在看代码--4
我在看代码--5
我在学习多线程--2
我在看代码--6
我在看代码--7
我在看代码--8
我在学习多线程--3
我在学习多线程--4
我在看代码--9
我在学习多线程--5
我在看代码--10
我在看代码--11
我在学习多线程--6
我在看代码--12
我在学习多线程--7
我在看代码--13
我在学习多线程--8
我在看代码--14
我在学习多线程--9
我在看代码--15
我在学习多线程--10
我在看代码--16
我在学习多线程--11
我在看代码--17
我在学习多线程--12
我在看代码--18
我在学习多线程--13
我在看代码--19
我在学习多线程--14
我在学习多线程--15
我在学习多线程--16
我在学习多线程--17
我在学习多线程--18
我在学习多线程--19

进程已结束,退出代码0
*/

下面为一个多线程图片保存的实例:

package 多线程;

import org.apache.commons.io.FileUtils;

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

//练习Thread,实现多线程同步下载图片
public class PicLoad extends Thread{
    private String url;
    private String name;

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

    @Override
    public void run() {
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url, name);
        System.out.println("下载了文件名为:" + name);
    }
}
//下载器
class WebDownloader{
    //下载方法
    public void downloader(String url, String name){
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            System.out.println("IO异常,downloader方法出现问题");
        }
    }
}
class Test{
    public static void main(String[] args) {
        PicLoad p1 = new PicLoad("https://pic1.zhimg.com/80/v2-86c0da0f749a46a1a7a2c7b80465d530_720w.jpg?source=1940ef5c", "qzy.jpg");
        PicLoad p2 = new PicLoad("https://ts1.cn.mm.bing.net/th?id=OIP-C.5eGvoxrfiCR8-F954A0g2gAAAA&w=146&h=170&c=8&rs=1&qlt=90&o=6&dpr=1.25&pid=3.1&rm=2", "bqb.jpg");
        PicLoad p3 = new PicLoad("https://ts1.cn.mm.bing.net/th?id=OIP-C.q5iRYT4Q_g_r7UBqAON9UwAAAA&w=146&h=170&c=8&rs=1&qlt=90&o=6&dpr=1.25&pid=3.1&rm=2", "xzc.jpg");
        p1.start();
        p2.start();
        p3.start();
    }
}
/*
结果为:
下载了文件名为:qzy.jpg
下载了文件名为:xzc.jpg
下载了文件名为:bqb.jpg
*/

结果成功保存了三张图片,但是保存顺序发生了变化,即为多线程的理念。

方法二:实现Runnable接口(推荐)

定义MyRunna类实现Runna接口

实现run()方法,编写线程执行体

创建线程对象,调用start()方法启动线程 

package 多线程;
//方法二:实现Runnable接口,重写run方法,执行线程需要丢入Runnable接口实现类,调用start开启线程
public class Create2 implements Runnable{
    @Override
    public void run() {
        //run方法线程体
        for(int i = 0;i<20;++i){
            System.out.println("我在看代码--" + i);
        }
    }

    public static void main(String[] args) {
        //创建Runnable接口的实现类对象
        Create2 create2 = new Create2();
        //创建线程对象,通过线程对象来开启我们的线程,代理
        Thread thread = new Thread(create2);
        thread.start();
        //new Thread(create2).start();//二者等价
        for (int i = 0; i < 20; i++) {
            System.out.println("我在学习多线程--" + i);
        }
    }
}

 输出结果也为穿插输出

 抢票实例:

package 多线程;

public class Create3 implements Runnable{
    //票数
    private int ticketNums = 10;

    @Override
    public void run() {
        while (true){
            if(ticketNums<=0){
                break;
            }
            System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "票");
        }
    }

    public static void main(String[] args) {
        Create3 ticket = new Create3();
        new Thread(ticket, "小明").start();
        new Thread(ticket, "老师").start();
        new Thread(ticket, "黄牛党").start();
    }
}
/*
黄牛党拿到了第10票
黄牛党拿到了第7票
黄牛党拿到了第6票
黄牛党拿到了第5票
小明拿到了第8票
小明拿到了第3票
老师拿到了第9票
老师拿到了第1票
小明拿到了第2票
黄牛党拿到了第4票
*/

龟兔赛跑实例:

package 多线程;
//龟兔赛跑模拟
public class Rabbit implements Runnable{

    //胜利者
    private static String winner;
    public void run(){
        for (int i = 0; i <= 100; i++) {
            //模拟兔子休息
            if(Thread.currentThread().getName().equals("兔子")&&i%10==0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //判断比赛是否结束
            boolean flag = gameover(i);
            if(flag){
                break;
            }
            System.out.println(Thread.currentThread().getName() + "跑了" + i + "步");
        }
    }
    //判断是否完成比赛
    private boolean gameover(int steps){
        //判断是否有胜利者
        if(winner!=null){//已经存在胜利者了
            return true;
        }
        if(steps>=100){
            winner = Thread.currentThread().getName();
            System.out.println("winner is " + winner);
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        Rabbit rabbit = new Rabbit();
        new Thread(rabbit, "兔子").start();
        new Thread(rabbit, "乌龟").start();
    }
}
//无论怎样都是乌龟获胜

方法三:实现Callable接口

实现方法:

实现Callable接口,设置返回值

重写call方法,并抛出异常

创建目标对象 

进入主函数......

package 多线程;

import org.apache.commons.io.FileUtils;

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

//方法三:实现callable接口
/*
可以定义返回值
可以抛出异常
实现复杂
*/
public class TestCallable implements Callable<Boolean> {
        private String url;
        private String name;

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

        public Boolean call() {
            WebDownloader webDownloader = new WebDownloader();
            webDownloader.downloader(url, name);
            System.out.println("下载了文件名为:" + name);
            return true;
        }

        public static void main(String[] args) throws Exception, InterruptedException{
            TestCallable t1 = new TestCallable("https://pic1.zhimg.com/80/v2-86c0da0f749a46a1a7a2c7b80465d530_720w.jpg?source=1940ef5c", "qzy.jpg");
            TestCallable t2= new TestCallable("https://ts1.cn.mm.bing.net/th?id=OIP-C.5eGvoxrfiCR8-F954A0g2gAAAA&w=146&h=170&c=8&rs=1&qlt=90&o=6&dpr=1.25&pid=3.1&rm=2", "bqb.jpg");
            TestCallable t3 = new TestCallable("https://ts1.cn.mm.bing.net/th?id=OIP-C.q5iRYT4Q_g_r7UBqAON9UwAAAA&w=146&h=170&c=8&rs=1&qlt=90&o=6&dpr=1.25&pid=3.1&rm=2", "xzc.jpg");
            //创建执行服务:
            ExecutorService ser = Executors.newFixedThreadPool(3);
            //提交执行服务:
            Future<Boolean> result1 = ser.submit(t1);
            Future<Boolean> result2 = ser.submit(t2);
            Future<Boolean> result3 = ser.submit(t3);
            //获取结果:
            boolean rs1 = result1.get();
            boolean rs2 = result2.get();
            boolean rs3 = result3.get();
            //关闭服务
            ser.shutdownNow();
        }
    }
class WebDownloader{
    //下载方法
    public void downloader(String url, String name){
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            System.out.println("IO异常,downloader方法出现问题");
        }
    }
}
/*
结果为:
下载了文件名为:bqb.jpg
下载了文件名为:xzc.jpg
下载了文件名为:qzy.jpg
*/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值