多线程学习笔记

简介: 这篇文章是多线程学习笔记,涵盖了线程与进程的概念、多线程实现方式、线程状态、线程同步与不安全示例、死锁问题以及生产者与消费者问题等多线程编程的关键知识点。

多线程学习笔记

一、线程 | 进程 | 多线程

线程:操作系统能够进行运算调度的最小单位、线程是独立调度和分派的基本单位、同一进程中的多条线程将共享该进程中的全部系统资源。一个进程可以有很多线程,每条线程并行执行不同的任务

进程:系统进行资源分配和调度的基本单位(进程是执行程序的一次过程。程序:是指令和数据的有序集合,静态)

多线程:从软件或者硬件上实现多个线程并发执行的技术。(并发:时间片轮回,每隔一段时间处理不同的程序,时间短。并行:同时运行)

二、创建方式

1、继承Thread类

  • 继承Thread类
  • 重写run方法
  • 开启线程
package com.zheng.demo1;

public class MyThread1 extends Thread {
    public static void main(String[] args) {
        MyThread1 myThread1 = new MyThread1();
        myThread1.start();

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

    }

    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("我是子线程====" + i);
        }
    }
}

测试结果:
在这里插入图片描述
线程开启后,主线程和子线程交替执行

2、实现runnable接口

  • 实现Runnable接口
  • 重写run()方法,编写线程执行体
  • 创建线程对象,开启线程
package com.zheng.demo1;

public class MyRunnable implements Runnable {
    public static void main(String[] args) {

        MyRunnable myRunnable = new MyRunnable();
//        Thread thread = new Thread(myRunnable);
//        thread.start();

        new Thread(myRunnable).start();

        for (int i = 0; i < 200; i++) {
            System.out.println("-------我是主线程------" + i);

        }
    }

    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("我是子线程------" + i);

        }
    }
}

测试结果
在这里插入图片描述
当创建多个对象,都开启线程。每一个对象都会独享一份重写run()里的资源

package com.zheng.demo1;

public class MyRunnable implements Runnable {
    public static void main(String[] args) {

        MyRunnable myRunnable = new MyRunnable();
        MyRunnable myRunnable1 = new MyRunnable();
        MyRunnable myRunnable2 = new MyRunnable();
        MyRunnable myRunnable3 = new MyRunnable();

        new Thread(myRunnable).start();
        new Thread(myRunnable1).start();
        new Thread(myRunnable2).start();
        new Thread(myRunnable3).start();

        for (int i = 0; i < 200; i++) {
            System.out.println("-------我是主线程------" + i);

        }
    }

    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("我是子线程------" + i);

        }
    }
}

测试结果
在这里插入图片描述
不安全购票行为

package com.zheng.demo1;

public class MyRunnable2 implements Runnable {
    private int ticket = 9;


    public static void main(String[] args) {
        MyRunnable2 station = new MyRunnable2();
        new Thread(station, "小明").start();
        new Thread(station, "老师").start();
        new Thread(station, "商贩").start();

    }


    public void run() {
        while (true) {
            if (ticket <= 0) {
                break;//跳出循环
            }
            try {
                Thread.sleep(1000);//模拟延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "买了第" + ticket-- + "张票");
        }
    }


}

测试结果
在这里插入图片描述

龟兔赛跑问题

package com.zheng.demo1;

public class Game implements Runnable {
    //只能有一个胜利者
    private String winner;

    public void run() {
        for (int i = 0; i <= 100; i++) {
            boolean flag = gameOver(i);
            if (Thread.currentThread().getName() == "兔子" && i % 10 == 0) {
                try {
                    Thread.sleep(5);//模拟兔子休眠
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
            if (!flag) {
                System.out.println(Thread.currentThread().getName() + "跑了" + i + "米");
            } else {
                break;
            }
        }

    }


    //判断比赛是否结束
    public boolean gameOver(int step) {
        if (winner != null) {
            return true;
        } else {
            //继续跑
            if (step >= 100) {
                winner = Thread.currentThread().getName();//得到当前线程对象名
                System.out.println("胜利者是:" + winner);
                return true;
            }
        }

        return false;
    }

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

}

测试结果
在这里插入图片描述

3、Callable接口
4、lambda

三、静态代理

真实对象和代理对象都要实现同一个接口,代理对象要代理真实对象,真实对象关注自己主要的事情

package com.zheng.demo2;

public interface Wedding {
    public void wedding();

}

//真实角色
class You implements Wedding {
    public You() {
    }

    public void wedding() {
        System.out.println("结婚了好开心");
    }
}

//代理角色
class WeddingCompang implements Wedding {
    private Wedding target;
    public WeddingCompang(Wedding target) {
        this.target = target;//要结婚的对象
    }

    public void wedding() {
        before();
        target.wedding();
        after();
    }

    public void before() {
        System.out.println("进行婚礼布置");
    }

    public void after() {
        System.out.println("进行婚后的处理");
    }

}
package com.zheng.demo2;

public class Mytest {
    public static void main(String[] args) {
        Wedding you = new You();
        WeddingCompang wc = new WeddingCompang(you);
        wc.wedding();

    }
}

测试结果
在这里插入图片描述

四、Lamda表达式

1、函数式接口(Function interface)

  • 定义:只包含一个抽象方法
public interface Runnable{
    public abstract void run();
}

【1】

package com.zheng.demo3;


//定义函数式接口
interface A {
    public void say();
}

class B implements A {
    public void say() {
        System.out.println("我是函数式接口");
    }

}

public class Lamda {
    public static void main(String[] args) {
        B b = new B();
        b.say();

    }

}

静态内部类:静态内部类定义在类中,任何方法外,用static定义。

package com.zheng.demo3;


//定义函数式接口
interface A {
    public void say();
}

public class Lamda {

    //静态内部类
    static class B1 implements A {
        public void say() {
            System.out.println("我是函数式接口1");
        }

    }

    public static void main(String[] args) {
        B1 b = new B1();
        b.say();

    }

}

在这里插入图片描述
局部内部类:写在方法里边的类

package com.zheng.demo3;


//定义函数式接口
interface A {
    public void say();
}

public class Lamda {

    public static void main(String[] args) {
        //局部内部类
        class B2 implements A {
            public void say() {
                System.out.println("我是函数式接口2");
            }

        }

        B2 b = new B2();
        b.say();

    }

}

在这里插入图片描述
匿名内部类:匿名类是不能有名字的类,它们不能被引用,只能在创建时用New语句来声明它们

package com.zheng.demo3;


//定义函数式接口
interface A {
    public void say();
}

public class Lamda {

    public static void main(String[] args) {

        //匿名内部类,没有类的名称,必须借助父类或者接口
     A a=new A() {
         public void say() {
             System.out.println("我是匿名内部类");
         }
     };

     a.say();



    }

}

在这里插入图片描述
lamda表达式

package com.zheng.demo3;


//定义函数式接口
interface A {
    public void say();
}

public class Lamda {

    public static void main(String[] args) {
        //使用lamda表达式
        A a1 = () -> {
            System.out.println("我是lamda表达式");
        };

        a1.say();


    }

}

在这里插入图片描述

五、线程状态

1、新建状态

  • 2、就绪状态
  • 3、运行状态
  • 4、阻塞状态
  • 5、死亡状态
    在这里插入图片描述
    线程停止:(使用标志位)
package com.zheng.demo4;

public class ThreadStop implements Runnable {
    boolean flag = true;

    public void run() {
        int i = 0;
        while (flag) {
            System.out.println("我是子线程======" + i++);
        }

    }

    //设置标志位用来停下线程
    public void stop() {
        this.flag = false;
    }

    public static void main(String[] args) {
        ThreadStop threadStop = new ThreadStop();
        new Thread(threadStop).start();

        for (int i = 0; i < 50; i++) {
            System.out.println("======我是主线程=====" + i);
            if (i == 25) {
                threadStop.stop();
                System.out.println("子线程停止了");
            }

        }
    }

}

测试结果
在这里插入图片描述
1、线程休眠:模拟网络延时,放大问题发生的可能性,找到问题

    try {
                Thread.sleep(1000);//模拟延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

打印当前的年月日时间

package com.zheng.demo4;

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

public class DateSleep {
    public static void main(String[] args) {
        //获取系统当前时间
        Date date = new Date(System.currentTimeMillis());

        while (true){
            try {
                Thread.sleep(1000);//休眠一秒钟
                String date1=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
                System.out.println(date1);
                date = new Date(System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在这里插入图片描述
2、线程礼让,让当前执行的线程先暂停转化为就绪状态

package com.zheng.demo4;

public class MyYield {
    public static void main(String[] args) {
        YieldMy my = new YieldMy();
        new Thread(my,"小明").start();
        new Thread(my,"老师").start();

    }
}

class YieldMy implements Runnable{

    public void run() {
        System.out.println(Thread.currentThread().getName()+"开始执行");
        Thread.yield();//线程礼让
        System.out.println(Thread.currentThread().getName()+"结束执行");

    }
}

在这里插入图片描述
3、线程的加入(join):加入的线程执行结束后,在执行其他的线程,其他线程阻塞

package com.zheng.demo4;

public class TestJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("我是加入的线程" + i);

        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();//开启线程

        for (int i = 0; i < 20; i++) {
            System.out.println("我是主线程" + i);
            if (i == 15) {
                thread.join();
            }

        }
    }
}

在这里插入图片描述
4、线程优先级

package com.zheng.demo4;

public class TestPriority {


    public static void main(String[] args) {
        Priority priority = new Priority();
        Thread t1 = new Thread(priority);
        Thread t2 = new Thread(priority);
        Thread t3 = new Thread(priority);
        Thread t4 = new Thread(priority);

        Thread t6 = new Thread(priority);
        Thread t7 = new Thread(priority);
        Thread t8 = new Thread(priority);
        Thread t9 = new Thread(priority);
        Thread t10 = new Thread(priority);

        System.out.println("当前线程名称:" + Thread.currentThread().getName() + "    优先级:" + Thread.currentThread().getPriority());


        //设置线程优先级
        t1.setPriority(Thread.MIN_PRIORITY);
        t10.setPriority(Thread.MAX_PRIORITY);

        t2.setPriority(2);
        t3.setPriority(3);
        t4.setPriority(4);

        t6.setPriority(6);
        t7.setPriority(7);
        t8.setPriority(8);
        t9.setPriority(9);

        //开启线程
        t1.start();
        t2.start();
        t3.start();
        t4.start();

        t6.start();
        t7.start();
        t8.start();
        t9.start();
        t10.start();
    }

}

class Priority implements Runnable {
    public void run() {
        //打印当前线程名称+优先级
        System.out.println("当前线程名称:" + Thread.currentThread().getName() + "    优先级:" + Thread.currentThread().getPriority());
    }
}

测试结果
在这里插入图片描述
优先级高代表CPU调度到的概率大。优先级低代表CPU调度到的概率低

5、守护线程
用户线程和守护线程,虚拟机必须保证用户线程执行完毕。不需要等待守护线程执行结束

package com.zheng.demo4;

public class Test {
    public static void main(String[] args) {
        People people = new People();
        Animal animal = new Animal();
        Thread thread = new Thread(animal);
        thread.setDaemon(true);//默认是false为用户线程,true改为守护线程
        thread.start();
        new Thread(people).start();
    }
}

class People implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("开心的生活");

        }
    }
}


class Animal implements Runnable {

    @Override
    public void run() {
        while (true) {
            System.out.println("动物守护着你");
        }
    }
}

测试结果
在这里插入图片描述
守护线程不会一直执行下去,当用户线程结束后,虚拟机关闭

六、线程同步不安全实例

package com.zheng.demo5;


public class BuyTicket {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(ticket, "小明").start();
        new Thread(ticket, "老师").start();
        new Thread(ticket, "商贩").start();
    }
}

class Ticket implements Runnable {

    private int ticket = 15;  //票数
    boolean flag = true;//设置标志位

    public void buyTicket() {
        if (ticket > 0) {//有票可以继续买
            System.out.println(Thread.currentThread().getName() + "买了第" + ticket-- + "张票");
        } else {
            //无票
            flag = false;//改变标志位状态,结束线程
        }

    }

    @Override
    public void run() {
        while (flag) {
            try {
                Thread.sleep(50);//模拟买票延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            buyTicket();//调用买票的方法
        }
    }
}

在这里插入图片描述
2、存钱取钱问题

package com.zheng.demo5;

public class Bank {
    public static void main(String[] args) {
        Account account = new Account(100, "买房");
        withdrawMoney thread = new withdrawMoney(account,50,"小明");
        withdrawMoney thread1 = new withdrawMoney(account,100,"小黑");
        thread.start();
        thread1.start();
    }
}


class Account{
    int money;
    String name;
    public Account(int money,String name){
        this.money=money;
        this.name=name;
    }

}

class withdrawMoney extends Thread{

     Account bank;

    int nowMoney;
    //取款金额
    int takeMoney;
    withdrawMoney(Account bank,int takeMoney,String name){
        super(name);
        this.bank=bank;
        this.takeMoney=takeMoney;

    }

    @Override
    public void run() {

        //判断是否能取钱
        if(bank.money-takeMoney<0){
            System.out.println("余额不够");
            return;
        }else {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
         bank.money=bank.money-takeMoney;
         nowMoney=nowMoney+takeMoney;
            System.out.println("账户还剩下"+bank.money);
            System.out.println(Thread.currentThread().getName()+"手里的钱"+nowMoney);

        }

    }
}

在这里插入图片描述
同步锁
买票

package com.zheng.demo5;


public class BuyTicket {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(ticket, "小明").start();
        new Thread(ticket, "老师").start();
        new Thread(ticket, "商贩").start();
    }
}

class Ticket implements Runnable {

    private int ticket = 15;  //票数
    boolean flag = true;//设置标志位

    //同步方法
    public synchronized void buyTicket() {
        if (ticket > 0) {//有票可以继续买
            System.out.println(Thread.currentThread().getName() + "买了第" + ticket-- + "张票");
        } else {
            //无票
            flag = false;//改变标志位状态,结束线程
        }

    }

    @Override
    public void run() {
        while (flag) {
            try {
                Thread.sleep(50);//模拟买票延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            buyTicket();//调用买票的方法
        }
    }
}

在这里插入图片描述
取钱

同步块:synchronized(obj){}
obj:共享资源作为同步监视器

  • 第一个线程访问,锁定,访问结束,第二个线程访问,锁定。。。
package com.zheng.demo5;

public class Bank {
    public static void main(String[] args) {
        Account account = new Account(100, "买房");
        withdrawMoney thread = new withdrawMoney(account,50,"小明");
        withdrawMoney thread1 = new withdrawMoney(account,100,"小黑");
        thread.start();
        thread1.start();
    }
}


class Account{
    int money;
    String name;
    public Account(int money,String name){
        this.money=money;
        this.name=name;
    }

}

class withdrawMoney extends Thread{

     Account bank;

    int nowMoney;
    //取款金额
    int takeMoney;
    withdrawMoney(Account bank,int takeMoney,String name){
        super(name);
        this.bank=bank;
        this.takeMoney=takeMoney;

    }

    @Override
    public void run() {
        synchronized (bank){
            //判断是否能取钱
            if(bank.money-takeMoney<0){
                System.out.println("余额不够");
                return;
            }else {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                bank.money=bank.money-takeMoney;
                nowMoney=nowMoney+takeMoney;
                System.out.println("账户还剩下"+bank.money);
                System.out.println(Thread.currentThread().getName()+"手里的钱"+nowMoney);

            }
        }



    }
}

在这里插入图片描述

七、死锁

多个线程各自占有一些共享资源,并且只有等待其他线程释放资源才能运行。A:我需要你给我钱,我才能给你货。B:我需要你给我货,我才能给你钱。

八、生产者和消费者问题

生产者将商品放入仓库,消费者从仓库中取走商品。

  • 仓库没有商品,生产者将商品放入仓库。有商品,则停止生产并且等待。直到消费者全部拿走
  • 仓库有商品,消费者取走,无商品,停止消费并且等待,生产者生产商品放入仓库
package com.zheng.demo6;

public class CustomerAndProducer {
    public static void main(String[] args) {
        Warehouse warehouse = new Warehouse();
        new Producer(warehouse).start();
        new Customer(warehouse).start();
    }
}


//生产者
class Producer extends Thread {
    Warehouse warehouse;

    public Producer(Warehouse warehouse) {
        this.warehouse = warehouse;
    }

    //生产
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("生产了" + i + "个商品");
            warehouse.push(new Food(i));

        }

    }
}


//消费者
class Customer extends Thread {
    Warehouse warehouse;

    public Customer(Warehouse warehouse) {
        this.warehouse = warehouse;
    }

    //消费
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("消费了" + warehouse.pop().id + "个商品");


        }

    }
}

//产品
class Food {
    int id;

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

//缓冲区
class Warehouse {
    //生产者仓库大小
    Food[] foods = new Food[10];
    //计数器
    int count = 0;

    //生产者放入商品
    public synchronized void push(Food food) {
        //先判断能否放入
        if (count == foods.length) {
            //已放满,通知消费者消费
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            //没有放满,继续放入产品
            foods[count] = food;
            count++;
            this.notify();//通知消费者消费
        }
    }

    //消费者消费商品
    public synchronized Food pop() {
        //先判断能否消费
        if (count == 0) {
            //等待生产者生产
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //可以消费
        count--;
        Food food = foods[count];
        //通知生产者生产
        this.notifyAll();


        return food;
    }


}

在这里插入图片描述

相关文章
|
6月前
|
网络协议 Linux C++
Linux C/C++ 开发(学习笔记十一 ):TCP服务器(并发网络网络编程 一请求一线程)
Linux C/C++ 开发(学习笔记十一 ):TCP服务器(并发网络网络编程 一请求一线程)
132 0
|
1月前
|
编解码 数据安全/隐私保护 计算机视觉
Opencv学习笔记(十):同步和异步(多线程)操作打开海康摄像头
如何使用OpenCV进行同步和异步操作来打开海康摄像头,并提供了相关的代码示例。
76 1
Opencv学习笔记(十):同步和异步(多线程)操作打开海康摄像头
|
1月前
FFmpeg学习笔记(二):多线程rtsp推流和ffplay拉流操作,并储存为多路avi格式的视频
这篇博客主要介绍了如何使用FFmpeg进行多线程RTSP推流和ffplay拉流操作,以及如何将视频流保存为多路AVI格式的视频文件。
170 0
|
6月前
|
NoSQL 网络协议 关系型数据库
redis-学习笔记(redis 单线程模型)
redis-学习笔记(redis 单线程模型)
55 3
|
6月前
|
安全 Java 编译器
多线程 (下) - 学习笔记2
多线程 (下) - 学习笔记
45 1
|
6月前
|
存储 算法 Java
多线程 (下) - 学习笔记1
多线程 (下) - 学习笔记
47 1
|
6月前
|
设计模式 安全 NoSQL
多线程 (上) - 学习笔记2
多线程 (上) - 学习笔记
46 1
|
6月前
|
Java 数据库连接 程序员
【后台开发】TinyWebser学习笔记(2)线程池、数据库连接池
【后台开发】TinyWebser学习笔记(2)线程池、数据库连接池
68 4
|
6月前
多线程学习笔记(一)
创建线程有3种方式:继承Thread类、实现Runnable接口或Callable接口。继承Thread类时,重写run()方法并调用start()启动线程。实现Runnable接口时,实现run()方法,通过Thread的target创建线程对象并用start()启动。
30 1
|
6月前
|
Java API 调度
多线程 (上) - 学习笔记1
多线程 (上) - 学习笔记
40 0