多线程(上篇)

多线程的意义:

​ 1.多线程的存在,不是提高程序的执行速度。其实是为了提高应用程序的使用率。

​ 2.程序的执行其实都是在抢CPU的资源,CPU的执行权

​ 3.多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权。

​ 4.我们是不敢保证哪一个线程能够在哪个时刻抢到,所以线程的执行有随机性。

介绍

进程(Process)

进程:进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。

进程具有的特征

动态性:进程是程序的一次执行过程,是临时的,有生命期的,是动态产生,动态消亡的;

并发性:任何进程都可以同其他进行一起并发执行;

独立性:进程是系统进行资源分配和调度的一个独立单位;

结构性:进程由程序,数据和进程控制块三部分组成

线程(Thread)

线程:线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位

创建线程

继承Thread.class

image-20200823184624853

调用run()

源程序
package lesson01;

/**
 * @author job knight
 * @date 2020/8/23 -18:56
 */
public class TestThread01 extends Thread{
    @Override
    public void run() {
        super.run();
        for (int i = 0;i<10;i++){
            System.out.println("我在看书");
        }

    }
    public static void main(String[] args) {
        TestThread01 thread01 = new TestThread01();
        thread01.run();
        for (int i = 0;i<10;i++){
            System.out.println("我在看电视");
        }
    }
}

输出结果

我在看书
我在看书
我在看书
我在看书
我在看书
我在看书
我在看书
我在看书
我在看书
我在看电视
我在看电视
我在看电视
我在看电视
我在看电视
我在看电视
我在看电视
我在看电视
我在看电视
我在看电视

调用start()

源代码
package lesson01;

/**
 * @author job knight
 * @date 2020/8/23 -18:56
 */
public class TestThread01 extends Thread{
    @Override
    public void run() {
        super.run();
        for (int i = 0;i<10;i++){
            System.out.println("我在看书");
        }

    }
    public static void main(String[] args) {
        TestThread01 thread01 = new TestThread01();
        thread01.start();
        for (int i = 0;i<200;i++){
            System.out.println("我在看电视");
        }
    }
}

输出结果(部分)

我在看电视
我在看电视
我在看书
我在看书
我在看书
我在看书
我在看书
我在看书
我在看书
我在看书
我在看书
我在看书
我在看电视
我在看电视
我在看电视

总结

线程开启不一定立刻执行,而是由cpu统一调度后执行

案例

多线程下载多个网图,主要思想:通过commons中的下载方法写到线程中,开启多个线程对象。

工具类Untils,使用commons-io-2.7.jar包中的

package lesson01;

import org.apache.commons.io.FileUtils;

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

/**
 * @author job knight
 * @date 2020/8/24 -14:37
 */
public class Utils {
    public void JpgDownload(String url,String filename){
        try {
            //调用commons中的FileUtils的下载方法
            FileUtils.copyURLToFile(new URL(url),new File(filename));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println(e+"download有错误");
        }
    }
}

下载类

package lesson01;

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

/**
 * @author job knight
 * @date 2020/8/24 -14:36
 */
public class JpgDownload extends Thread {
    private String url;
    private String file;
	//构造方法
    public JpgDownload(String url, String file) {
        this.url = url;
        this.file = file;
    }
	//线程方法
    @Override
    public void run() {
        Utils utils = new Utils();
        utils.JpgDownload(url, file);
    }
	
    public static void main(String[] args) {
        //线程一
        Thread t1 = new Thread(new JpgDownload("http://pic.netbian.com/uploads/allimg/200817/224555-1597675555c35d.jpg",
                "224555-1597675555c35d.jpg"));
        //线程二
        Thread t2 = new Thread(new JpgDownload("http://pic.netbian.com/uploads/allimg/200618/005100-1592412660f973.jpg",
                "005100-1592412660f973.jpg"));
        t1.start();
        t2.start();
    }

}

实现Runnable接口(推荐使用)

实现Runnable接口后,new 出对象,然后new一个Thread对象放入对象

特点

突破单继承的局限性,灵活方便,可以同一个对象被多个线程使用

package lesson01;

/**
 * @author job knight
 * @date 2020/8/24 -21:54
 */
public class TestThread02 implements Runnable {
    @Override
    //Run方法线程体
    public void run() {
        for (int i = 0;i<10;i++){
            System.out.println("我在看书");
        }
    }
    public static void main(String[] args) {
        //创建Runnable接口实现类
        TestThread02 tt02 = new TestThread02();
        //创建线程对象,通过线程对象来开启我们的线程,代理
        new Thread(tt02).start();
        for (int i = 0;i<200;i++){
            System.out.println("我在看电视");
        }
    }
}

并发问题

火车票问题

源码

package lesson01;

/**
 * @author job knight
 * @date 2020/8/24 -22:15
 */
//火车站抢票问题
public class TestThread03 implements Runnable{
    private int ticketNum =10;
    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (ticketNum<=0){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"-->第"+ticketNum--+"张票");
        }
    }

    public static void main(String[] args) {
        //三个线程执行一行代码,CPU单个执行,速度过快看起来像多线程。每一个线程对象,都轮流运行一次,
        TestThread03 tt03 = new TestThread03();
        new Thread(tt03,"小王").start();
        new Thread(tt03,"小李").start();
        new Thread(tt03,"黄牛").start();
    }
}

输出结果

image-20200824222643176

问题

两个人同时买到了同一张票,比如小李和小王同时买到第四张

龟兔赛跑

我的代码(bug=》产生两个胜利者)

package lesson01;

import java.text.BreakIterator;

/**
 * @author job knight
 * @date 2020/8/25 -17:37
 */
public class TestRabbitWithTurtle implements Runnable{
    private int road = 0;
    @Override
    public void run() {
        while (true) {

            if (road >= 100) {
                System.out.println(Thread.currentThread().getName()+"赢了!!!");
                break;
            }
            System.out.println(Thread.currentThread().getName() + "跑了" + (road++) + "步");
        }
    }

    public static void main(String[] args) {
        TestRabbitWithTurtle trwt = new TestRabbitWithTurtle();

        new Thread(trwt,"乌龟").start();
        new Thread(trwt,"兔子").start();
    }
}

老师代码

package lesson01;

/**
 * @author job knight
 * @date 2020/8/25 -17:51
 */
public class Race implements Runnable {
    private String winner;
    @Override
    public void run() {
        for (int i = 0;i<=100; i++){
            //判断比赛是否结束
            boolean flag = gameOver(i);
            if (flag){
                break;
            }
            System.out.println(Thread.currentThread().getName() + "跑了" + i +"步");
        }

    }
    //判断是否有胜利者
    private boolean gameOver(int step){
        //如果有胜利者就可以返回,如果此处不写会导致有两个胜利者
        if (winner != null){
            return true;
        }else if (step == 100){
            //此时产生胜利者并输出打印
            winner = Thread.currentThread().getName();
            System.out.println("Winner is "+winner);
            return true;
        }
            return false;


    }
    public static void main(String[] args) {
        Race race = new Race();

        new Thread(race,"乌龟").start();
        new Thread(race,"兔子").start();

    }

}

静态代理

代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。

为什么叫做静态呢?因为它的类型是事先预定好的,

案例1 电影院问题

接口

public interface Movie{
    void play();
}

真正的电影

class RealMovie implements Movie{

    @Override
    public void play() {
        System.out.println("播放电影");
    }
}

电影院(代理人)

public class  Cinema implements mover  {
    private RealMovie realmovie;

    public Cinema(lesson02.RealMovie realmovie) {
        this.realmovie = realmovie;
    }

    private void befor(){
        System.out.println("前排出售花生瓜子矿泉水");
    }

    private void after(){
        System.out.println("电影后收拾垃圾");
    }


    @Override
    public void play() {

        befor();
        System.out.println("播放电影");
        after();

    }

}

调用

public class Task{
    public static void main(String[] args) {
        // 静态代理
        RealMovie realmovie = new RealMovie();
        Cinema proxy = new Cinema(realmovie);
        proxy.play();
    }
}

Lamda表达式

特点:

避免内部类定义过多

让代码看起来更简洁

去掉无意义的代码,只留下核心的逻辑

其实质是函数式编程的概念

进化历史

package lesson02;

//执行任务
public class Task{
    //2、静态内部类
    static class Imover implements Movie{

        @Override
        public void play() {
            System.out.println("播放电影");
        }
    }

    public static void main(String[] args) {

        //3、局部内部类
        class Imover2 implements Movie {

            @Override
            public void play() {
                System.out.println("播放电影");
            }
        }

        //4、匿名内部类
        Movie mover3 = new Movie() {
            @Override
            public void play() {
                System.out.println("播放电影");
            }
        };

        //6、lamda实现方式
        Movie movie = ()->{
            System.out.println("播放电影");
        };
        movie.play();
    }

}

使用简化

package lesson02;

/**
 * @author job knight
 * @date 2020/8/31 -16:52
 */
public class lamdaDemo02 {
    public static void main(String[] args) {
        //1、lamda简化
        mover imover = ( int a)->{
            System.out.println("movie"+a);
        };
        //2、省掉括号
        mover imover02 = a -> {
            System.out.println("movie"+a);
        };
        //3、省掉花括号
        mover imover03 = a-> System.out.println("movie"+a);
    }
}

总结

  • 在代码只有一行且为函数式编程时才能够省略花括号
  • 在有多个参数时需要省略参数类型要省略都省略,而且必须添加括号

停止线程

原文地址:https://www.cnblogs.com/yuknight/p/13740793.html