【Java (一:10-1) 多线程学习】

本文详细介绍了Java中多线程的实现方式,包括继承Thread类、实现Runnable接口、Callable和Future接口,以及线程的命名和管理。同时,讨论了线程安全问题,如死锁、同步代码块、同步方法和Lock锁的使用,通过案例展示了如何解决并发中的数据一致性问题。此外,还探讨了生产者消费者模式及其在阻塞队列中的实现。
摘要由CSDN通过智能技术生成

一、 多线程

1. 并行与并发

并行:同一时刻,有多个指令在多个CPU上同时执行
并发:同一时刻,有多个指令在单个CPU上交替执行

2.进程和线程

进程:正在运行的软件
在这里插入图片描述

线程:进程中的单个顺序控制流,是一条执行路径
在这里插入图片描述

3. *****多线程的实现方式

在这里插入图片描述

3.1 继承Thread类的方式进行实现

在这里插入图片描述

public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("线程开启"+i);
        }
    }
    public static void main(String[] args) {
        MyThread t1=new MyThread();
        MyThread t2=new MyThread();
        t1.start();
        t2.start();
    }
}
  • 为什么要重写run()方法?
    多线程开启之后执行run()方法,run()是用来封装被线程执行的代码
  • run()方法和start()方法的区别?
    run方法调用不开启线程,start开启线程
3.2 实现Runnable接口方式进行实现

在这里插入图片描述

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("线程开启了"+i);
        }
    }

    public static void main(String[] args) {
        MyRunnable mr=new MyRunnable();
        Thread t1=new Thread(mr);
        t1.start();
        MyRunnable mrs=new MyRunnable();
        Thread t2=new Thread(mrs);
        t2.start();
    }
}

3.3 利用Callable和Future接口方式实现

在这里插入图片描述

public class MyCallable implements Callable<String> {

    @Override
    public String call() throws Exception {
        for (int i = 0; i < 50; i++) {
            System.out.println("向女孩表白"+i);
        }
        return "答应";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable mt=new MyCallable();
        FutureTask<String> ft=new FutureTask<String>(mt);
        Thread t=new Thread(ft);
        t.start();

        String s = ft.get();
        System.out.println(s);

    }
}

get方法在线程开启之后调用
三种方式对比
在这里插入图片描述

3.4 设置获取线程名字
  1. 获取线程名字
public class Mythread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName()+"****"+i);
        }
    }

    public static void main(String[] args) {
        Mythread mt=new Mythread();
        mt.start();
        Mythread ss=new Mythread();
        ss.start();
    }
}
  1. 设置线程名字
  • set形式
        Mythread mt=new Mythread();
        mt.start();
        mt.setName("线程1");
        Mythread ss=new Mythread();
        ss.start();
        ss.setName("线程2");
  • 构造器形式
public class Mythread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName()+"****"+i);
        }
    }

    public Mythread(String name) {
        super(name);
    }

    public static void main(String[] args) {
        Mythread mt=new Mythread("线程1");
        mt.start();

        Mythread ss=new Mythread("线程2");
        ss.start();

    }
}
4.获得线程对象

在runnable中可以获取线程对象名字

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName()+"running"+i);
        }
    }

    public static void main(String[] args) {
//        MyRunnable myRunnable=new MyRunnable();
//        Thread thread=new Thread(myRunnable);
//        thread.setName("666");
//        thread.start();
        String name = Thread.currentThread().getName();
        System.out.println(name);

    }
}

5.线程休眠

Thread.sleep(3000);

 @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName()+"running"+i);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

6.线程调度[线程的优先级]

在这里插入图片描述
在这里插入图片描述
线程的优先级用数字表示,范围从1~10
Thread.MIN_PRIORITY = 1; 线程获得最小优先级
Thread.MAX_PRIORITY = 10; 线程获得最大优先级
Thread.NORM_PRIORITY = 5; 分配给线程的默认优先级

public class MyCallable implements Callable<String> {

    @Override
    public String call() throws Exception {
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName()+"***"+i);
        }
        return "线程执行结束";
    }

    public static void main(String[] args) {
        MyCallable mc=new MyCallable();
        FutureTask<String> ft=new FutureTask<>(mc);
        Thread thread=new Thread(ft);
        thread.start();
        thread.setPriority(2);
        thread.setName("飞机");
//        System.out.println(thread.getPriority());



        MyCallable mc2=new MyCallable();
        FutureTask<String> ft2=new FutureTask<>(mc2);
        Thread t2=new Thread(ft2);
        t2.start();
        t2.setPriority(10);
        t2.setName("坦克");
//        System.out.println(t2.getPriority());
    }
}

7.后台线程/守护线程

普通线程结束 守护线程也随之结束

public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName()+"****"+i);
        }
    }

    public MyThread(String name) {
        super(name);
    }

    public static void main(String[] args) {
        MyThread mt=new MyThread("女神");
        mt.start();
//当普通线程执行完后 守护线程也没有继续运行下去的必要了
        MyThread2 t2=new MyThread2("备胎");
        t2.setDaemon(true);//该线程为守护线程
        t2.start();
    }
}
public class MyThread2 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName()+"****"+i);
        }
    }

    public MyThread2(String name) {
        super(name);
    }
}

二、 线程安全问题

1. 案例

在这里插入图片描述


public class Ticket implements  Runnable{
    private  int ticket=100;
    @Override
    public void run() {
        while (true){
            if (ticket<=0){
                break;
            }else {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                ticket--;
                System.out.println(Thread.currentThread().getName()+"在卖票,"+"还剩"+ticket+"张票");
            }
        }
    }

    public static void main(String[] args) {
        Ticket ticket=new Ticket();
        Thread t1=new Thread(ticket);
        Thread t2=new Thread(ticket);
        Thread t3=new Thread(ticket);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

出现问题:窗口卖了 同一张票给多个人 且出现了负票的现象,后面使用同步方法块进行同步
在这里插入图片描述

2. 同步代码块

在这里插入图片描述
private Object obj=new Object();
synchronized(obj){}


public class Ticket implements  Runnable{
    private  int ticket=100;
    private  Object obj=new Object();
    @Override
    public void run() {
            while (true){
                synchronized (obj){//多个线程必须使用同一把锁
                if (ticket<=0){
                    break;
                }else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ticket--;
                    System.out.println(Thread.currentThread().getName()+"在卖票,"+"还剩"+ticket+"张票");
                }
            }
        }

    }

    public static void main(String[] args) {
        Ticket ticket=new Ticket();
        Thread t1=new Thread(ticket);
        Thread t2=new Thread(ticket);
        Thread t3=new Thread(ticket);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

通过同步代码块解决上述问题,没有重复票和负号票
在这里插入图片描述

3. 锁对象唯一

synchronized中的锁对象要唯一


public class MyThread extends  Thread{
    private static  int ticketCount = 100;
    private static  Object obj=new Object();
    @Override
    public void run() {
       while (true){
           synchronized (obj){ //当前线程对象
               if (ticketCount <= 0) {
                   break;
               } else {
                   try {
                       Thread.sleep(100);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   ticketCount--;
                   System.out.println(Thread.currentThread().getName() + "在卖票,还剩" + ticketCount + "张票");
               }
           }
       }
    }

    public static void main(String[] args) {
        MyThread t1=new MyThread();
        MyThread t2=new MyThread();
        t1.setName("窗口1");
        t2.setName("窗口2");
        t1.start();
        t2.start();

    }
}

4. 同步方法

对于普通同步方法,锁是当前实例对象。
对于静态同步方法,锁是当前类的Class对象。
对于同步方法块,锁是Synchonized括号里配置的对象。
同步方法的锁对象是this
synchronized的使用

  1. 同步代码块的地方
  2. 直接加在方法上;变成同步方法
    在这里插入图片描述

public class Myrunnable implements Runnable {
    private int ticketCount = 100;

    @Override
    public void run() {
        while (true) {
            if ("窗口1".equals(Thread.currentThread().getName())) {
                //同步方法
                boolean result = synchronizedMthod();
                if (result) {
                    break;
                }
            }

            if ("窗口2".equals(Thread.currentThread().getName())) {
                synchronized (this) {
                    if (ticketCount == 0) {
                        break;
                    } else {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        ticketCount--;
                        System.out.println(Thread.currentThread().getName() + "在卖票," + "还剩" + ticketCount + "张票");
                    }

                }

            }


        }
    }

    private synchronized boolean synchronizedMthod() {
        if (ticketCount == 0) {
            return true;
        } else {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ticketCount--;
            System.out.println(Thread.currentThread().getName() + "在卖票," + "还剩" + ticketCount + "张票");
            return false;

        }
    }

    public static void main(String[] args) {
        Myrunnable mr=new Myrunnable();
        Thread t1=new Thread(mr);
        Thread t2=new Thread(mr);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t1.start();
        t2.start();

    }
}

5. 同步静态方法

同步静态方法的锁对象是 类名.class
在这里插入图片描述


public class MyRunnable implements Runnable {
    private  static  int ticketCount = 100;

    @Override
    public void run() {
        while (true) {
            if ("窗口1".equals(Thread.currentThread().getName())) {
                //同步方法
                boolean result = synchronizedMethod();
                if (result) {
                    break;
                }
            }
            if ("窗口2".equals(Thread.currentThread().getName())) {
                //同步代码块
                synchronized (MyRunnable.class) {
                    if (ticketCount == 0) {
                        break;
                    } else {
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        ticketCount--;
                        System.out.println(Thread.currentThread().getName() + "在卖票,还剩" + ticketCount + "张票");
                    }
                }
            }


        }
    }

    private static synchronized boolean synchronizedMethod() {
        if (ticketCount == 0) {
            return true;
        } else {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ticketCount--;
            System.out.println(Thread.currentThread().getName() + "在卖票,还剩" + ticketCount + "张票");
            return false;
        }
    }

    public static void main(String[] args) {

        MyRunnable mr = new MyRunnable();
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t1.start();
        t2.start();
    }


}
6. Lock锁

在这里插入图片描述


import java.util.concurrent.locks.ReentrantLock;

public class Ticket implements Runnable {
    private int ticket = 100;
    private Object obj = new Object();
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();
                // synchronized (obj){
                if (ticket <= 0) {
                    break;
                } else {
                    Thread.sleep(10);

                    ticket--;
                    System.out.println(Thread.currentThread().getName() + "在卖票," + "还剩" + ticket + "张票");
                    //   }
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

    }

    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread t1 = new Thread(ticket);
        Thread t2 = new Thread(ticket);
        Thread t3 = new Thread(ticket);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

7. 死锁

死锁eg


public class Test {
    public static void main(String[] args) {
        Object objA=new Object();
        Object objB=new Object();
        new Thread(()->{
            while (true){
                synchronized (objA){
                    synchronized (objB){
                        System.out.println("A running");
                    }
                }
            }
        }).start();
        new Thread(()->{
            while (true){
                synchronized (objB){
                    synchronized (objA){
                        System.out.println("B running");
                    }
                }
            }
        }).start();
    }
}

三、生产者与消费者

生产者消费模式是一个十分经典的多线程协作模式,假设有 厨师生产汉堡,顾客吃汉堡,以及桌子放汉堡这样一个场景,进行多线程实现。
在这里插入图片描述

1. 生产者与消费者_代码实现

public class Chef extends  Thread{

    /**
     * 生产者步骤
     *  判断是否桌子上有汉堡
     *  如果有就等待,没有就生产
     *   将汉堡放在桌子上
     *   唤醒消费者吃汉堡
     */
    @Override
    public void run() {
        while (true){
            synchronized (Desk.lock){
                //没有汉堡包了
                if (Desk.count==0){
                    break;
                }else{
                    //桌子上没有汉堡包就生产
                    if (!Desk.flag){
                        System.out.println("厨师正在生产汉堡包");
                        Desk.flag=true;
                        Desk.lock.notifyAll();
                    }else{
                        //桌子上有汉堡包,等待
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                    }
                }
            }
        }
    }
}

public class Costumer extends  Thread{

    /**
     * 消费者步骤
     * 判断桌子上是否有汉堡
     *  如果没有就等待
     *   如果有就开吃
     *   吃完之后,桌子上的汉堡没有了
     *   唤醒等待的生产者生产汉堡
     *   汉堡总数-1
     */
    @Override
    public void run() {
        //套路: 1.while(true) 死循环
        //2.synchronized() 锁对象要唯一
        //3.判断 共享数据是否结束 结束
        //4.判断 共享数据是否结束 没有结束
        while (true){
            synchronized(Desk.lock){
                //还有没有汉堡包
                if (Desk.count==0){
                    break;
                }else{
                    //桌子上有汉堡包
                    if (Desk.flag){
                        System.out.println("顾客在吃汉堡包");
                        Desk.flag=false;
                        Desk.lock.notifyAll();
                        Desk.count--;
                    }else{
                        //桌子上没有汉堡包,就等待
                        //锁对象来调用wait()
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }

            }
        }
    }
}

public class Desk {
    //定义一个标记
    //判断桌子上是否有汉堡
    //true 有汉堡包 顾客执行
    //false 没有汉堡包 厨师执行
    public static boolean flag = false;
    // 汉堡包总数量
    public static int count = 10;

    //锁对象
    public static  final  Object lock = new Object();

}

public class Demo {
    /**
     * 消费者步骤
     * 判断桌子上是否有汉堡
     *  如果没有就等待
     *   如果有就开吃
     *   吃完之后,桌子上的汉堡没有了
     *   唤醒等待的生产者生产汉堡
     *   汉堡总数-1
     */

    /**
     * 生产者步骤
     *  判断是否桌子上有汉堡
     *  如果有就等待,没有就生产
     *   将汉堡放在桌子上
     *   唤醒消费者吃汉堡
     */
    public static void main(String[] args) {
        Costumer costumer=new Costumer();
        Chef chef=new Chef();
        chef.start();
        costumer.start();
    }

}


2. 生产者与消费者_代码改进实现

public class Desk {
    //定义一个标记
    //判断桌子上是否有汉堡
    //true 有汉堡包 顾客执行
    //false 没有汉堡包 厨师执行
    // public static boolean flag = false;
    // 汉堡包总数量
    // public static int count = 10;
    // public static  final  Object lock = new Object();
    private boolean flag;

    private int count;

    //锁对象

    private final Object lock = new Object();

    public Desk() {
        this(false,10);
    }

    public Desk(boolean flag, int count) {
        this.flag = flag;
        this.count = count;
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public Object getLock() {
        return lock;
    }

    @Override
    public String toString() {
        return "Desk{" +
                "flag=" + flag +
                ", count=" + count +
                ", lock=" + lock +
                '}';
    }
}


public class Chef extends  Thread{
    private  Desk desk;

    public Chef(Desk desk) {
        this.desk=desk;
    }

    /**
     * 生产者步骤
     *  判断是否桌子上有汉堡
     *  如果有就等待,没有就生产
     *   将汉堡放在桌子上
     *   唤醒消费者吃汉堡
     */
    @Override
    public void run() {
        while (true){
            synchronized (desk.getLock()){
                //没有汉堡包了
                if (desk.getCount()==0){
                    break;
                }else{
                    //桌子上没有汉堡包就生产
                    if (!desk.isFlag()){
                        System.out.println("厨师正在生产汉堡包");
                        desk.setFlag(true);
                        desk.getLock().notifyAll();
                    }else{
                        //桌子上有汉堡包,等待
                        try {
                            desk.getLock().wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                    }
                }
            }
        }
    }
}

public class Costumer extends  Thread{
    private Desk desk;

    public Costumer(Desk desk) {
      this.desk=desk;
    }

    /**
     * 消费者步骤
     * 判断桌子上是否有汉堡
     *  如果没有就等待
     *   如果有就开吃
     *   吃完之后,桌子上的汉堡没有了
     *   唤醒等待的生产者生产汉堡
     *   汉堡总数-1
     */
    @Override
    public void run() {
        //套路: 1.while(true) 死循环
        //2.synchronized() 锁对象要唯一
        //3.判断 共享数据是否结束 结束
        //4.判断 共享数据是否结束 没有结束
        while (true){
            synchronized(desk.getLock()){
                //还有没有汉堡包
                if (desk.getCount()==0){
                    break;
                }else{
                    //桌子上有汉堡包
                    if (desk.isFlag()){
                        System.out.println("顾客在吃汉堡包");
                        desk.setFlag(false);
                        desk.getLock().notifyAll();
                        desk.setCount(desk.getCount()-1);
                    }else{
                        //桌子上没有汉堡包,就等待
                        //锁对象来调用wait()
                        try {
                            desk.getLock().wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }

            }
        }
    }
}

public class Demo {
    /**
     * 消费者步骤
     * 判断桌子上是否有汉堡
     *  如果没有就等待
     *   如果有就开吃
     *   吃完之后,桌子上的汉堡没有了
     *   唤醒等待的生产者生产汉堡
     *   汉堡总数-1
     */

    /**
     * 生产者步骤
     *  判断是否桌子上有汉堡
     *  如果有就等待,没有就生产
     *   将汉堡放在桌子上
     *   唤醒消费者吃汉堡
     */
    public static void main(String[] args) {

        Desk  desk=new Desk();
        Costumer costumer=new Costumer(desk);
        Chef chef=new Chef(desk);
        chef.start();
        costumer.start();
    }

}

3. 阻塞队列_基本写法

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述


public class Demo_ {
    public static void main(String[] args) throws InterruptedException {
        //阻塞队列实现
        //创建阻塞队列对象,容量为2
        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(2);

        //存储元素
        arrayBlockingQueue.put("汉堡包");
        arrayBlockingQueue.put("热狗");


        //取元素
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        
        //因为队列中只有两个元素 所以当取出后  现在为阻塞状态
        System.out.println(arrayBlockingQueue.take());
        System.out.println("程序运行结束");
    }
}

在这里插入图片描述

4. 阻塞队列_实现唤醒阻塞

ArrayBlockingQueue 的take和put方法中的源码是加了锁的

在这里插入图片描述

实现代码


public class Chef  extends  Thread{
    private  ArrayBlockingQueue<String> list;
    public Chef(ArrayBlockingQueue<String> list) {
        this.list=list;
    }

    @Override
    public void run() {
        while (true) {
            try {
                list.put("汉堡包");
                System.out.println("Chef 放了一个汉堡包");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Costumer extends  Thread{
    private  ArrayBlockingQueue<String> list;
    public Costumer(ArrayBlockingQueue<String> list) {
        this.list=list;
    }

    @Override
    public void run() {
        while (true){
            try {
                String take = list.take();
                System.out.println("Costumer 吃了一个"+ take);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}


public class Test {
    public static void main(String[] args) throws InterruptedException {
        //创建一个阻塞队列 容量为1
        ArrayBlockingQueue<String> list=new ArrayBlockingQueue<>(1);
        //创建线程并开启
        Costumer costumer=new Costumer(list);
        Chef chef=new Chef(list);
        chef.start();
        costumer.start();

    }
}

打印结果:
这里打印有问题的原因在于:输出语句没有在锁中,因此打印可能不会呈现效果,但是程序是正常执行的,获取锁然后释放锁
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱喝阔落的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值