【Java多线程】线程实现

前言

本系列记录Java从入门开始的知识点,多线程详解:线程简介,线程实现。


一、线程简介

1、Process与Thread

  1. 程序:程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
  2. 进程:执行程序的一次执行过程,是一个动态的概念,进程是系统资源分配的单位
  3. 线程:通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程。线程是CPU调度和执行的单位

2、核心概念

  1. 线程是独立的执行路径;
  2. 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
  3. main()称之为主线程,为系统的入口,用于执行整个程序;
  4. 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序时不能人为干预的;
  5. 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
  6. 线程会带来额外的开销,如CPU调度时间,并发控制开销;
  7. 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。

二、线程实现

1、三种线程创建方式

在这里插入图片描述

2、继承Thread类

将一个类声明为Thread的子类。 这个子类应该重写run类的方法Thread 。 然后可以分配并启动子类的实例.

  1. start()是同时执行,run()不是。
//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
public class TestThread1 extends Thread{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("看run==="+i);
        }
    }

    public static void main(String[] args) {
        //main线程,主线程
        TestThread1 testThread1 = new TestThread1();
        testThread1.start();

        for (int i = 0; i < 2000; i++) {
            System.out.println("看主线程==="+i);
        }
    }
}

左:start,右:run。

  1. 练习Thread,实现多线程同步下载图片
    (1)下载commons-io.jar包;
    (2)创建lib包,把commons-io.jar包放入lib中,并导入;

    (3)编写代码
package Thread.demo01;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;

//练习Thread,实现多线程同步下载图片
public class TestThread2 extends Thread{

    private String url;
    private String name;

    public TestThread2(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);
    }

    public static void main(String[] args) {
        TestThread2 t1 = new TestThread2("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic4.zhimg.com%2F50%2Fv2-8431442783f60d9efc2ffda7e04e8691_r.jpg&refer=http%3A%2F%2Fpic4.zhimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1654483941&t=6a2e2aa2fae724a90400672ab949aef8","cat1.jpg");
        TestThread2 t2 = new TestThread2("https://img2.baidu.com/it/u=4076046515,2235174071&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500","cat2.jpg");
        TestThread2 t3 = new TestThread2("https://img2.baidu.com/it/u=2080085769,843387130&fm=253&fmt=auto&app=138&f=JPEG?w=501&h=500","cat3.jpg");

        t1.start();
        t2.start();
        t3.start();
    }
}

//下载器
class WebDownloader{
    public void downloader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常");
        }
    }
}

在这里插入图片描述

3、实现Runnable接口

创建一个线程是声明实现类Runnable接口。 那个类然后实现了run方法。 然后可以分配类的实例,在创建Thread时作为参数传递,并启动。其实和Thread很像,只是要将Thread最为参数传递进去。

//创建线程方式2:实现runnable接口,重写run方法,执行线程需要丢入runnable接口的实现类,调用start方法。
public class TestThread3 implements Runnable{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("看run==="+i);
        }
    }

    public static void main(String[] args) {
        //创建runnable接口的实现类对象
        TestThread3 testThread3 = new TestThread3();

        //创建线程对象,通过线程对象来开启我们的线程,代理
//        Thread thread = new Thread(testThread3);
//        thread.start();
        new Thread(testThread3).start();

        for (int i = 0; i < 2000; i++) {
            System.out.println("看主线程==="+i);
        }
    }
}

4、小结Thread类和Runnable接口

在这里插入图片描述

5、火车票例子(并发问题)

public class TestThread5 implements Runnable{

    private int tickeNums = 10;

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

    public static void main(String[] args) {
        TestThread5 ticket = new TestThread5();
        new Thread(ticket,"法外狂徒张三").start();
        new Thread(ticket,"不明真相李四").start();
        new Thread(ticket,"实在冤枉王五").start();
    }
}

在这里插入图片描述

6、龟兔赛跑例子

模拟龟兔赛跑中乌龟一直跑,兔子中途睡觉,故事中是乌龟赢,所以要模拟兔子睡觉。
(1)首先需要一个赛道距离,然后要离终点越来越近。
(2)判断比赛是否结束。
(3)打印出胜利者。
(4)龟兔赛跑开始。
(5)模拟兔子睡觉。

public class Race implements Runnable{
    private static String winner;

    @Override
    public void run() {
        for (int i=1;i<=100;i++){
            if(Thread.currentThread().getName().equals("兔子") && i%10==0){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if(!gameOver(i)) {
                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 + "赢了");
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        Race race = new Race();
        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}

在这里插入图片描述

7、实现Callable接口(了解即可)

在这里插入图片描述

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 TestThread6 implements Callable<Boolean> {
    private String url;
    private String name;

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

    @Override
    public Boolean call() {
        WebDownloader3 webDownloader3 = new WebDownloader3();
        webDownloader3.downloader(url,name);
        System.out.println("下载了文件名"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestThread6 t1 = new TestThread6("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic4.zhimg.com%2F50%2Fv2-8431442783f60d9efc2ffda7e04e8691_r.jpg&refer=http%3A%2F%2Fpic4.zhimg.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1654483941&t=6a2e2aa2fae724a90400672ab949aef8","cat1.jpg");
        TestThread6 t2 = new TestThread6("https://img2.baidu.com/it/u=4076046515,2235174071&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=500","cat2.jpg");
        TestThread6 t3 = new TestThread6("https://img2.baidu.com/it/u=2080085769,843387130&fm=253&fmt=auto&app=138&f=JPEG?w=501&h=500","cat3.jpg");

        ExecutorService ser = Executors.newFixedThreadPool(3);

        Future<Boolean> r1 = ser.submit(t1);
        Future<Boolean> r2 = ser.submit(t2);
        Future<Boolean> r3 = ser.submit(t3);

        boolean rs1 = r1.get();
        boolean rs2 = r2.get();
        boolean rs3 = r3.get();

        ser.shutdownNow();
    }
}
class WebDownloader3{
    public void downloader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常");
        }
    }
}

callable的好处:

(1)可以定义返回值;
(2)可以抛出异常.

8、Lambda表达式

public class TestLambda2 {

    public static void main(String[] args) {
        //lambda表达式简化
        ILove love = (int a) -> {
                System.out.println("i love you" + a);
        };
//        love.love(2);

        //简化1.参数类型
        love = (a) -> {
            System.out.println("i love you" + a);
        };
//        love.love(2);

        //简化2.简化括号
        love = a -> {
            System.out.println("i love you" + a);
        };
//        love.love(21);

        //简化3.去掉花括号(前提是代码只有一行)
        love = a -> System.out.println("i love you" + a);;
        love.love(521);
    }
}

interface ILove{
    void love(int a);
}

总结:

(1)lambda表达式只能有一行代码的情况下才能简化成为一行,如果有多行,那么就用代码块包裹。
(2)前提是接口为函数式接口。
(3)多个参数也能去掉参数类型,要去掉就都去掉,要加上括号。

9、静态代理

public class StacticProxy {
    public static void main(String[] args) {
        WeddingCompany weddingCompany  =new WeddingCompany(new You());
        weddingCompany.HappyMarry();
    }
}

interface Marry{
    void HappyMarry();
}


class You implements Marry{
    @Override
    public void HappyMarry() {
        System.out.println("Marry!!");
    }
}

class WeddingCompany implements Marry{

    private Marry target;

    public WeddingCompany(Marry target) {
        this.target = target;
    }

    @Override
    public void HappyMarry() {
        before();
        this.target.HappyMarry();
        after();
    }

    private void after() {
        System.out.println("结婚后");
    }

    private void before() {
        System.out.println("结婚前");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值