多线程的学习

多线程的学习(java.Thread)

线程的简介

任务、进程、线程、多线程

多任务

如:边吃饭边玩手机,就是多任务,但其实本质上我们的大脑还是只进行一个任务,上一秒吃饭,下一秒玩手机,因为转换太快了所以忽略了。

多线程

如:一条道路分有多条道,汽车并排骑行也不会有问题,这就是多线程。

如:五个人同时开黑打游戏,游戏实现了多线程。

普通方法调用和多线程

1、普通方法执行效率低

2、多线程执行效率高

程序进程和线程

系统中我们运行的程序就是进程,如:QQ、微信和B站这些就是进程,如:播放视频同时有图像、声音、弹幕这些是同时执行线程的结果,也就是多线程。

进程(Process)、线程(Thread)

程序:是指令和数据的有序集合,没有运行的含义,是一个静态概念。

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

通常一个进程中可以包含多个线程(同时有图像、声音、弹幕),一个进程至少有一个线程,线程是cpu调度和执行的单位。

注意:很多多线程是模拟出来的,真正的多线程是指由多个cpu,即多核。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换很快,所以就有同时执行的错觉

核心概念

1、线程是独立执行的路径

2、程序运行时,即使没有自己创建线程,后台也会有多个线程,如:主线程、gc线程。

3、main()称之为主线程,为系统的入口,用户执行整个程序。

4、进程中有多个线程,线程的运行由调度器安排调度,调度器与操作系统紧密相关,先后顺序不能人为干预。

5、对同一资源操作时,存在资源抢夺问题,需要并发控制。

6、线程会带来额外的开销,如:cpu调度时间,并发控制开销。

7、每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。

线程创建

class Thread

继承Object类

实现Runnable接口类

线程是程序执行中的线程,java虚拟机允许程序同时执行多个执行线程

创建步骤:

1、自定义线程类继承Tread类。

2、重写run()方法,编写线程执行体。

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

public class StartThread extends Thread{
    // 线程入口点
    @Override
    public void main(){
        ...
    }
}
public static void main(String[] args) {
    // 创建线程对象
    StartThread t = new StrartThread();
    t.start();
}

网图下载

1、配置环境

1.1下载jar包:Apache commons-io

2、将jar添加到程序中

2.1 删除导入的commons-io jar包

2.2 添加jar包到项目中

3、代码

通过继承线程实现,图片下载

package com.zhou.demo01;

import org.apache.commons.io.FileUtils;

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

public class TestThread1 extends Thread{
    private String url; // 网络图片路径
    private String name; // 文件名称

    // 构造方法
    public TestThread1(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) {
        TestThread1 t1 = new TestThread1("http://bpic.51yuansu.com/activity/20200408/5e8d446443d63.jpg","1.jpg");
        TestThread1 t2 = new TestThread1("http://bpic.51yuansu.com/activity/20200326/5e7c19d39bf8d.jpg","2.jpg");
        TestThread1 t3 = new TestThread1("http://bpic.51yuansu.com/activity/20191025/5db2c3d021879.jpg","3.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异常,downloader方法出现问题!");
            }
        }
    }
}

实现Runnable接口

package com.zhou.demo01;

import org.apache.commons.io.FileUtils;

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

public class TestThread2 implements Runnable{
    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("http://bpic.51yuansu.com/activity/20200408/5e8d446443d63.jpg","1.jpg");
        TestThread2 t2 = new TestThread2("http://bpic.51yuansu.com/activity/20200326/5e7c19d39bf8d.jpg","2.jpg");
        TestThread2 t3 = new TestThread2("http://bpic.51yuansu.com/activity/20191025/5db2c3d021879.jpg","3.jpg");

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

        new Thread(t1).start();
        new Thread(t2).start();
        new Thread(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异常,downloader方法出现问题!");
            }
        }
    }
}

4、小结

4.1 继承Thread类

​ 子类继承Thread类具备多项成能力

​ 启动线程:子类对象.start()

​ 不建议使用:避免oop单继承单继承局限性

4.2 实现Runnable接口

​ 实现接口Runnable具有多线程能力

​ 启动线程:传入目标对象+Thread对象.start()

推荐使用:避免但继承局限性,灵活方便,方便同一个对象呗多个线程使用

线程并发

1、多个线程操作一个对象

多个线程操作同一个资源的情况,线程不安全,数据混乱

package com.zhou.demo01;

// 多个线程操作同一个资源的情况先,线程不安全,数据混乱
public class TestThread4 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) {
        TestThread4 ticket = new TestThread4();

        new Thread(ticket,"小明").start();
        new Thread(ticket,"小红").start();
        new Thread(ticket,"小王").start();
    }
}

龟兔赛跑

package com.zhou.demo01;

public class TestThread5 implements Runnable{
    private static String winner;
    @Override
    public void run() {
        for(int i = 0;i <= 100;i++) {

            // 模拟兔子睡觉
            if (Thread.currentThread().getName().equals("兔子")) {
                try {
                    Thread.sleep(20);
                } 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;
        }else {
            if (steps >= 100) {
                winner = Thread.currentThread().getName();
                System.out.println("winner is " + winner);
                return true;
            }
        }
        return false;
    }

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

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

实现callable

静态代理

如:结婚,婚庆公司帮你处理结婚的事(现场布置安排),结婚:实现结婚接口即可。

静态代理总结:

1、真是角色和代理都要实现同一个接口

2、代理中有真是角色

好处

1、代理可以做真是角色做不了的事情

2、真实角色只需要专注做自己的事情

package com.zhou.demo2;

// 静态代理总结
// 真是角色和代理同时实现同一个接口
// 代理中要有真是角色

// 好处
//  代理可以做真是角色做不了的事情
// 真是角色只需要专注做自己的事情
public class Stacticproxy {
    public static void main(String[] args) {
        WeddingCompany weddingCompany = new WeddingCompany(new You());
        weddingCompany.HappanMarry();
    }
}

// 结婚接口
interface Marry{
    void HappanMarry();
}

class You implements Marry{

    @Override
    public void HappanMarry() {
        System.out.println("老哥结婚!");
    }
}

class WeddingCompany implements Marry{
    private Marry target;

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

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

    private void after() {
        System.out.println("结婚后,收尾款!");
    }

    private void before() {
        System.out.println("结婚前,安排结婚事项!");

    }
}

lamda表达式

为什么要用lamda表达式

简洁代码

lamda表达式代码

package com.zhou.Demo03;

public class TestLamda1 {

    // 2、内部静态类
    static class Like2 implements Ilike{

        @Override
        public void lamda() {
            System.out.println("I like lamda 2!");
        }
    }

    public static void main(String[] args) {
        // 1、执行外部类
        // 接口new一个实现类(like),则该接口的实现类都能够使用,如:2、执行内部.. (可以用)相当于
        // 如果是实现类new一个实现类,则变量只能在使用在这个实现类
        Ilike like = new Like();
        like.lamda();

        // 2、执行内部静态类
        Like2 like2 = new Like2();
        like2.lamda();
        // 相当于:
        // like = new Like2();
        // like.lamda();

        // 3、局部类
        class Like3 implements Ilike{

            @Override
            public void lamda() {
                System.out.println("I like lamda 3!");
            }
        }

        // 3、执行内部类
        Like3 like3 = new Like3();
        like3.lamda();

        // 4、匿名类
         Like like4 = new Like() {
             @Override
             public void lamda() {
                 System.out.println("I like lamda 4!");
             }
         };
         // 4、执行匿名类
         like4.lamda();


        // 5、用lamda简化
        Ilike like5 = () -> {
            System.out.println("I like lamda 5!");
        };
        // 5、执行lamda简化
        like5.lamda();

    }
}

interface Ilike {
    void lamda();
}

//  1、外部实体类
class Like implements Ilike{
    @Override
    public void lamda() {
        System.out.println("I like lamda!");
    }
}

线程停止

代码

package com.zhou.Demo04;

public class TestStop implements Runnable{
    private boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while (flag) {
            System.out.println("run..Thread--" + i++);
        }
    }

    // 设公开停止线程的方法
    public void stop() {
        this.flag = false;
    }

    public static void main(String[] args) {

        TestStop t = new TestStop();
        new Thread(t).start();

        for (int i =0;i < 1000;i++) {
            System.out.println("main.." + i);
            if (i == 900) {
                t.stop();
                System.out.println("线程停止!");
            }
        }
    }
}

slepp休眠

购票

package com.zhou.Demo05;

public class TestSleep implements Runnable {
    private int ticketNums = 10;

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

    public static void main(String[] args) {
        TestSleep testSleep = new TestSleep();
        new Thread(testSleep,"小明").start();
        new Thread(testSleep,"小红").start();
        new Thread(testSleep,"小王").start();

    }
}

倒计时

package com.zhou.Demo05;

import java.text.SimpleDateFormat;
import java.util.Date;

public class TestSleep1 {

    public static void tenDown() {
        int num = 10;
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(num--);
            if (num == 0) {
                break;
            }
        }
    }

    public static void main(String[] args) {
        // 倒数计时
        // tenDown();

        // 系统时间
        Date startTime = new Date(System.currentTimeMillis());
        while (true) {
            try {
                Thread.sleep(1000);
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                startTime = new Date(System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

线程礼让

假设有A、B线程,A、B同时运行,A得到cpu的调度,这时A礼让重新出来出来与B竞争,这时候谁能得到调度还是看cpu心情。

// 线程礼让
public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield,"吃饭").start();
        new Thread(myYield,"睡觉").start();
    }
}
class MyYield implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程开始!");
        Thread.yield();
        System.out.println(Thread.currentThread().getName() + "线程停止!");
    }
}

join(线程优先运行权)

线程中的vip,此线程执行完成后在执行其他线程。

public class TestJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0;i < 100;i++) {
            System.out.println("线程中的vip" + i);
        }
    }
    
    public static void main(String[] args) {
        TestJoin testjoin = new TestJoin();
        Thread thread = new Thread(testjoin);
        thread.start();

        // 主线程
        for (int i =0;i < 300;i++) {
            if (i == 200) {
                try {
                    thread.join(); // 线程vip先执行完这个,线程后,才会执行下一个线程
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("main" + i);
        }
    }
}

state(线程状态)

new之后是就绪状态,调度进入运行状态,之后进入阻塞状态或是结束状态(结束后就不能再次启动)。

public class TestState {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0;i < 5;i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("/////");
        });

        // 观察状态
        Thread.State state = thread.getState();
        System.out.println("观察状态:" +  state);

        // 观察启动后
        thread.start();
        state = thread.getState();
        System.out.println("启动后:" + state);

        while (state != Thread.State.TERMINATED) { // 只要线程不终止,就一直是输出状态
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            state = thread.getState(); // 更新线程状态
            System.out.println(state); // 输出状态
        }
        //thread.start(); // 结束后是不能再运行的
    }
}

优先级(不影响cpu的调度,越高资源越多)

public class Priority {
    public static void main(String[] args) {
        MyPriority myPriority = new MyPriority();
        Thread t1 = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        Thread t4 = new Thread(myPriority);
        Thread t5 = new Thread(myPriority);
        Thread t6 = new Thread(myPriority);

        t1.start(); // 默认线程优先级为:5

        t2.setPriority(1);
        t2.start();

        t3.setPriority(4);
        t3.start();

        t4.setPriority(7);
        t4.start();

        t5.setPriority(9);
        t5.start();

        t6.setPriority(Thread.MAX_PRIORITY); // 表示最高优先级即10
        t6.start();
    }
}

class MyPriority implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "线程的优先级:" + Thread.currentThread().getPriority());
    }
}

守护(daemon)线程:程序结束还会继续运行,虚拟机停止才会停止

  • 线程分为 用户线程守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕,虚拟机运行结束是守护线程也结束
  • 守护线程有:后台记录操作日志,监控内存,垃圾回收等待...

public class Deamon {
    public static void main(String[] args) {
        You you = new You();
        God god = new God();

        // 启动上帝线程
        Thread thread = new Thread(god);
        // 守护线程是程序结束后还会继续执行,虚拟机停止守护线程才会结束
        thread.setDaemon(true); //设置为true,是守护线程。默认为false(即用户线程)
        thread.start(); // 启动上帝守护线程,守护一生

        new Thread(you).start(); // 你启动线程
    }
}

// 上帝守护你(守护线程:垃圾回收等)
class God implements Runnable {

    @Override
    public void run() {
        while (true) {
            System.out.println("上帝守护!");
        }
    }
}

// 你
class You implements Runnable {

    @Override
    public void run() {
        // 人的一生差不多是100年
        for (int i = 0; i < 365000; i++) {
            System.out.println("你开心的一生!");
        }
        System.out.println("结束goodbye!");
    }
}

同步方法()

并发:同一对象被多个线程同时操作

队列:如人们吃饭排队一样,知识程序中年叫队列

等待池:如100个人进行排队的地址

队列锁:如电影中以前排队进入公共电话亭打电话,每一个进去的人都会锁住们不让他人进来。

锁机制(synckronized):安全性提高,但拖慢线程。

public class Ticket {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();

        new Thread(buyTicket,"苦逼的我").start();
        new Thread(buyTicket,"牛逼的你们").start();
        new Thread(buyTicket,"可恶的黄牛党").start();
    }
}

// 购票人
class BuyTicket implements Runnable {
    private int ticket = 10;
    boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // synchronized 同步方法 锁的是对象本身(购票人:BuyTicket)
    private synchronized void buy() throws InterruptedException {
        if (ticket <= 0) {
            flag = false;
            return ;
        }
        Thread.sleep(100);

        System.out.println(Thread.currentThread().getName() + "拿到第" + ticket-- + "票");
    }
}

死锁

如:两个小孩,一个小孩有一个玩具车、另一个小孩有玩具枪,都各自想要对方的玩具,自己手中的玩具也一直拿在自己手中,这时候就形成了死锁。

程序也一样,一个线程拥有了两个以上的死锁。

// 死锁 多线程互相把持对方所需资源,又不会换取,形成僵持
public class DeadLock {
    public static void main(String[] args) {
        Makeup g1 = new Makeup(0,"灰姑娘");
        Makeup g2 = new Makeup(1,"白雪公主");

        g1.start();
        g2.start();
    }
}

// 口红
class Lipstick {

}

// 镜子
class Mirror {

}

// 化妆
class Makeup extends Thread {
    // 需要的资源只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice; // 选择
    String girlName; // 使用化妆物品的人

    // 构造器
    Makeup(int choice,String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 化妆 互相只有对方所需资源
    private void makeup() throws InterruptedException {
        // 解开死锁
        // 互相持有对方所需资源,同时放开自己的资源,使用对方持有的资源
        if (choice == 0) {
            synchronized (lipstick) { // 获得口红锁
                System.out.println(this.girlName + ":获取口红");
                Thread.sleep(1000);
            }
            synchronized (mirror) { // 获得镜子锁
                System.out.println(this.girlName + ":获取镜子");
            }
        } else {
            synchronized (mirror) { // 获得镜子锁
                System.out.println(this.girlName + ":获取镜子");
                Thread.sleep(1000);
            }
            synchronized (lipstick) { // 获得口红锁
                System.out.println(this.girlName + ":获取口红");
            }
        }

/*        // 互相持有对方所需资源,自己的资源拿在手中但又都想获得对方资源,
        if (choice == 0) {
           synchronized (lipstick) { // 获得口红锁
               System.out.println(this.girlName + ":获取口红");
               Thread.sleep(1000);
                synchronized (mirror) { // 获得镜子锁
                    System.out.println(this.girlName + ":获取镜子");
                }
            }
        } else {
            synchronized (mirror) { // 获得镜子锁
                System.out.println(this.girlName + ":获取镜子");
                Thread.sleep(1000);
                synchronized (lipstick) { // 获得口红锁
                    System.out.println(this.girlName + ":获取口红");
                }
            }
        }*/
    }
}

public class LockTicket {
    public static void main(String[] args) {
        TestLock2 testLock2 = new TestLock2();

        new Thread(testLock2).start();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
    }
}

class TestLock2 implements Runnable{

    int ticketNums = 10;
    // 定义lock锁,可重用锁ReetrantLock
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            lock.lock(); //加锁,是程序进行排队,一个一个进行,知道结束。如果不加锁会出现负数
            try {
                if (ticketNums > 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(ticketNums--);
                } else {
                    break;
                }
            } finally {
                lock.unlock(); // 解锁
            }
        }
    }
} 

线程协作

生产者消费模式:如你去肯德基,你(消费者)到前台告诉服务员要一份童子鸡,前台通知厨师(生产者),厨师开始制作鸡完成后,给前台,前台给你。

管程法

// 测试:生产者消费者模型 -> 利用缓冲区解决:管程法
// 生产者 消费者 缓冲区
public class TestPC {
    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();

        new Productor(synContainer).start();
        new Consumer(synContainer).start();
    }
}

// 生产者
class Productor extends Thread{
    SynContainer container;

    public Productor(SynContainer container) {
        this.container = container;
    }

    // 生产
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                container.push(new Chicken(i));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("生产了" + i + "只鸡");
        }
    }
}

// 消费者
class Consumer extends Thread{
    SynContainer container;

    public Consumer(SynContainer container) {
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            try {
                System.out.println("消费了" + container.pop().id + "只鸡!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// 产品
class Chicken{
    int id; // 产品编号

    public Chicken(int id) {
        this.id = id;
    }
}

// 缓冲区
class SynContainer{
    // 需要一个容器大小
    Chicken[] chickents = new Chicken[10];
    // 容器计数器
    int count = 0;

    // 生产者放入产品
    public synchronized void push(Chicken chicken) throws InterruptedException {
        // 如果容器满了,就需要等待消费
        if (count == chickents.length) {
            // 通知消费者消费,生产等待
            this.wait();
        }

        // 如果没有满,我们就需要丢入产品
        chickents[count] = chicken;
        count++;

        // 可以通知消费者消费了
        this.notify();
    }

    // 消费者消费产品
    public synchronized Chicken pop() throws InterruptedException {
        // 判断能否消费
        if (count == 0) {
            // 等待生产者生产,消费者等待
            this.wait();
        }

        // 如果可以消费
        count--;
        Chicken chicken = chickents[count];

        // 吃完了,通知生产者生产
        this.notify();
        return chicken;
    }
}

线程池

// 测试线程池
public class TestPool {
    public static void main(String[] args) {
        // 1.创建服务,创建线程池
        // newFixedThreadPool 参数为:线程池大小
        ExecutorService service = Executors.newFixedThreadPool(10);

        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        // 2.关闭链接
        service.shutdown();
    }
}

class MyThread implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

学习观看的视频为:【狂神说Java】多线程详解

原文地址:https://www.cnblogs.com/zhouyongyin/p/12800163.html