多线程==

多线程是提升程序性能非常重要的一种方式
使用多线程可以让程序充分利用CPU资源
优点

  • 系统资源得到更合理的利用
  • 程序设计更加简洁
  • 程序响应更快、运行效率更高
    缺点
  • 需要更多的内存空间来支持多线程
  • 多线程并发访问的情况可能会影响数据的准确性
  • 数据被多线程共享,可能会出现死锁

进程和线程

什么是进程:就是计算正在运行的一个独立的应用程序。
进程是一个动态的概念,当我们启动某个应用的时候,进程就产生了,当我们关闭应用的时候,进程就结束了,进程的生命周期就是我们使用该软件的整个过程
什么是线程?
线程是组成进程的基本单位,可以完成特定的功能,一个进程是一个或多个线程组成的
应用程序时静态的,进程和线程时动态的,有创建有销毁,存在是暂时的,不是永久的。
进程和线程的区别
进程在运行时拥有独立的内存空间,每个进程所占用的内存空间是独立的,线程是共享内存空间的,但是每个线程的执行都是独立,单独的线程是无法执行的,由进程来控制多个线程的执行。

多线程

多线程是指一个进程中,多个线程同时执行,

系统会为每个线程分配CPU资源,在某个具体的时间段内CPU资源会被一个线程占用,在不同的时间段内由不同线程来占用CPU资源,所以多个线程是在交替执行

  • 注意
    不能通过run方法来调用线程的任务,因run方法调用相当于普通对象的执行,并不会去抢占CPU资源
    只有通过start方法才能开启线程,从而去抢占CPU资源,当某个线程抢占到CPU资源后,会自动调用run方法
线程和任务

线程是去抢占CPU资源
任务是具体执行业务逻辑
线程内部会包含一个任务
线程启动
当抢到资源后
任务开始执行

  • 继承Thread类和实现Runnable接口区别
    继承Thread方式,直接在类中重写Run方法,使用的时候直接实例化, 因Thread内部存在Runnable
    实现Runnanble接口方法,在实现类中重写Run方法,使用时需先创建Thread对象
    并将MyRunnable注入到Thread中
    Thread.start
  MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();

线程状态(5种)

在特定的情况下,线程可以在不同的状态之间切换

  • 创建状态:实例化一个新的线程对象,还未启动
  • 就绪状态:创建好的线程对象调用Start方法完成启动,进入线程池等待抢占CPU资源
  • 运行状态:线程对象获取了CPU资源,在一定时间内执行任务
  • 阻塞状态:正在运行的线程暂停执行任务,释放所占用的CPU资源,并在解除阻塞状态之后也不能直接回到运行状态,而是重新回到就绪状态,等待获取CPU资源
  • 终止状态:线程执行完成之后或因异常导致线程终止
    -在这里插入图片描述
线程调度
  • 线程休眠
    让当前线程暂停执行,从运行状态进入阻塞状态,将CPU资源让给其他线程的调度方式,通过sleep()来实现。
    sleep(long millis) 单位毫秒
    sleep是静态本地方法,可以通过类调用,也可也通过对象调用,方法定义抛出InterruptedException,InterruptedException继承Exception,外部调用时必须手动处理异常。
线程合并

合并是指将指定的某个线程加入到当前线程种,合并为一个线程,由两个线程交替执行变成一个线程中的两个线程顺序执行。
通过join方法来实现合并
线程A和线程B,线程A执行到某个时间点的时候调用线程B的join方法,则表示从当前时间点开始CPU资源被线程B独占,线程A进入阻塞状态,知道线程B执行完成,线程甲进入就绪状态,等待获取CPU资源进入运行状态
join方法重载,join()表示B线程执行完成之后才能执行其他线程
join(long millis)表示B线程执行millis毫秒之后,无论是否执行完毕,其他线程都可以和它争夺CPU资源

线程礼让

线程礼让是指某特定时间点,让线程暂停抢占CPU资源行为,运行状态/就绪状态----》阻塞状态,将CPU资源让给其他线程来使用
若线程A和线程B在交替执行,谋时间点线程A做出了礼让,则在这个时间节点线程B拥有CPU资源执行业务逻辑,但不代表线程A一直暂停执行
线程A只是在特定的时间节点礼让,过了时间节点,线程A再次进入就绪状态,和线程B争夺CPU资源
yield


public class YieldThread1 extends Thread{
    public void run(){
        for (int i = 0; i < 10; i++) {
            if( i == 5){
              yield();
            }
            System.out.println(Thread.currentThread().getName()+"-----"+i);
        }
    }
}
public class YieldThread2 extends Thread {
    public void run(){
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }
    }
}

public class Test {
    public static void main(String[] args) {

        YieldThread1 thread1 = new YieldThread1();
        thread1.setName("线程1");
        YieldThread2 thread2 =  new YieldThread2();
        thread2.setName("线程2");
        thread1.start();
        thread2.start();

    }
}
线程中断
  • 线程执行完毕自动停止
  • 线程执行过程中遇到错误抛出异常并停止
  • 线程执行过程中根据需求手动停止
    public void stop()
    public void interrupt()
    public boolean isInterrupted()
    interrupt是一个实例方法,当一个线程对象调用该方法时,表示中断当前线程对象
    每个线程对象都是通过一个标志位来判断当前是否为中断状态
    isInterrupted就是用来获取当前线程对象的标志位: true表示清除了标志位,当前线程已经终端;false表示没有清除标志位,当前对象没有中断
    当一个线程对象处于不同的状态时,中断机制也不同的。
public class Test {
    public static void main(String[] args) {
        Thread thread =  new Thread();
        //getState  当前线程的状态
        System.out.println(thread.getState());
        thread.interrupt();
        System.out.println(thread.isInterrupted());
    }
}

在这里插入图片描述
NEW当前线程为创建状态,false表示当前线程并未中断,因为当前线程没有启动,就不存在中断,故不需要清除标志位

public class Test2 {
    public static void main(String[] args) {
//        MyRunnable runnable = new MyRunnable();
//        Thread thread = new Thread(runnable);
//        thread.start();

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(i+"----main-");
                }
            }
        });
        thread.start();
        System.out.println(thread.getState());
        thread.interrupt();
        System.out.println(thread.isInterrupted());
        System.out.println(thread.getState());
    }
}
线程同步

多个线程同时操作同一个共享数据时,可能会导致数据不准确的问题
通过synchronized关键字修饰方法实现线程同步,每个java对象都有一个内置锁,内置锁会保护使用synchronized关键字的方法,要调用该方法就必须先获得锁,否则处于阻塞状态。
非线程同步

public class Account implements Runnable {
    //为了获取数据一致  所以用静态修饰
    private static  int num;
    @Override
    public void run() {
        try {
            Thread.currentThread().sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        num++;
        System.out.println(Thread.currentThread().getName()+"是当前第"+num+"位访客");
    }
}

线程同步

public class Account implements Runnable {
    //为了获取数据一致  所以用静态修饰
    private static  int num;
    @Override
    public synchronized void run() {
        try {
            Thread.currentThread().sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        num++;
        System.out.println(Thread.currentThread().getName()+"是当前第"+num+"位访客");
    }
}
public class Test {
    public static void main(String[] args) {
        Account account = new Account();
        Thread t1 = new Thread(account,"李四");
        Thread t2 = new Thread(account,"张三");
        t1.start();
        t2.start();

        for (int i = 0; i < 10; i++) {

            Thread thread = new Thread(account,"线程"+i);
            thread.start();
        }

    }
}

synchronized修饰非静态方法



public class SynchronizedTest2 {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    SynchronizedTest2 synchronizedTest2  = new SynchronizedTest2();
                            synchronizedTest2.test();
                }
            });
            thread.start();
        }
    }

    public synchronized void test(){
        System.out.println("start...");
        try {
            Thread.currentThread().sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end...");
    }
}

给实例方法(非静态方法)添加synchronized关键字并不能实现线程同步。

synchronized修饰静态方法


public class SynchronizedTest {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {

                    SynchronizedTest.test();
                }
            });
            thread.start();
        }
    }
    public synchronized static void test(){
        System.out.println("start....");
        try {
            Thread.currentThread().sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end....");
    }
}

线程同步本质是锁定多个线程所共享的资源
synchronized可以修饰代码块,会为代码块加上内置锁,从而实现同步。

package com.xmm.test;

/**
 * @Description TODO
 * @Author Xm
 * @Date 2022/7/16 15:18
 */

public class SynchronizedTest3 {
    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                  SynchronizedTest3.test();
                }
            });
            thread.start();
        }
    }
    public static void test(){
       synchronized (SynchronizedTest3.class){ System.out.println("...start");
            try {
                Thread.currentThread().sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("end...");}
    }
}

如何判断线程同步或不同
找到关键点:锁定的资源在内存中是一份还是多份 一份需要排队是同步 多份 不是线程同步
无论锁定方法还是锁定对象,锁定类,只需要分析这个方法、对象、类在内存中有几份即可
对象一般是多份
类一定是一份
方法看是静态还是非静态
静态一定是一份
非静态一般是多份

线程安全的单例模式

单例模式是一种常见的设计模式
核心思想是一个类只有一个实例对象

public class SingletonDemo {
    private static SingletonDemo singletonDemo;

    private SingletonDemo() {
        System.out.println("创建了SingletonDemo..");
    }

    public synchronized static SingletonDemo getInstance() {
        //判断对象是否已经被创建
        if (singletonDemo == null) {
            singletonDemo = new SingletonDemo();
        }
        return singletonDemo;
    }
}

public class Test2 {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
           SingletonDemo singletonDemo = SingletonDemo.getInstance();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                SingletonDemo singletonDemo = SingletonDemo.getInstance();
            }
        }).start();
    }
}

双重检测 synchronized修饰代码块

  • 线程同步是为了实现线程安全、如果只创建一个对象,那么线程就是安全的
  • 如果synchronized锁定的是多个线程共享的数据(同一个对象),那么线程就是安全的。
public class SingletonDemo {
    private volatile static SingletonDemo singletonDemo;
    
    private SingletonDemo() {
        System.out.println("创建了SingletonDemo..");
    }

    public  static SingletonDemo getInstance() {
        //判断对象是否已经被创建
        if(singletonDemo == null){
        synchronized(SingletonDemo.class){
        if (singletonDemo == null) {
            singletonDemo = new SingletonDemo();
        }
        }
        return singletonDemo;
    }
}

volatile的作用是可以使内存中的数据对线程可见
主内存对线程是不可见的,添加volatile关键字之后,主内存对线程可见
synchronized关键字实现线程同步,让在访问同一个资源的多个线程排队去完成业务,避免出现数据错乱的情况。

死锁
  • 前提:一个线程完成业务需要同时访问两个资源
  • 死锁: 多个线程同时完成业务,出现争抢资源的情况
    资源类 是供线程调用的

public class DeadLockRunnable implements Runnable {
//编号
    public int num;
    //资源
    private static  Chopsticks chopsticks1 = new Chopsticks();
    private static  Chopsticks chopsticks2 = new Chopsticks();

    /**、
     * num = 1 拿到  chopsticks1 , 等待 chopsticks2
     * num = 2 拿到  chopsticks2 , 等待 chopsticks1
*/
    @Override
    public void run() {
        if (num == 1) {
            System.out.println(Thread.currentThread().getName() + "拿到了chopsticks1,等待获取chopsticks2");
            synchronized (chopsticks1) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (chopsticks2) {
                    System.out.println(Thread.currentThread().getName() + "用餐完毕");
                }
            }
        }
        if (num == 2) {
            System.out.println(Thread.currentThread().getName() + "拿到chopsticks2,等待获取chopsticks1");
            synchronized (chopsticks2) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (chopsticks1) {
                    System.out.println(Thread.currentThread().getName() + "用餐完毕");

                }
            }


        }
    }
}

public class DeadLockTest {
    public static void main(String[] args) {
        DeadLockRunnable deadLockRunnable1 = new DeadLockRunnable();
        deadLockRunnable1.num = 1;
        DeadLockRunnable deadLockRunnable2 = new DeadLockRunnable();
        deadLockRunnable2.num = 2;
        new Thread(deadLockRunnable1,"张三").start();
        new Thread(deadLockRunnable2,"李四").start();
    }
}

如何破解死锁?
不让多线程并发访问


public class DeadLockTest {
    public static void main(String[] args) {
        DeadLockRunnable deadLockRunnable1 = new DeadLockRunnable();
        deadLockRunnable1.num = 1;
        DeadLockRunnable deadLockRunnable2 = new DeadLockRunnable();
        deadLockRunnable2.num = 2;
        new Thread(deadLockRunnable1,"张三").start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(deadLockRunnable2,"李四").start();
    }
}

Lambda表达式简化代码开发

    public static void main(String[] args) {
       new Thread(()->{
           for (int i = 0; i < 100; i++) {
               System.out.println("==========Runnable");
           }
       }).start();
    }
Lock

JUC
java.util.concurrent

Lock是一个接口,用来实现线程同步,功能与synchronized一样
Lock使用频率最高的实现类是ReentrantLock(重入锁),可也重复上锁



import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class Test {
    public static void main(String[] args) {
        Account account = new Account();
        new Thread(account,"A").start();
        new Thread(account,"B").start();
    }

}


class Account implements Runnable{
    private static  int num;
    private Lock lock = new ReentrantLock();
    @Override
    public  void run() {
        lock.lock();
        num++;
        System.out.println(Thread.currentThread().getName()+"当前的第"+num+"位访客");
        lock.unlock();

    }
}


实现资源和Runnable接口的解耦合操作

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test1 {
    public static void main(String[] args) {

        Acount acount = new Acount();
        new Thread(()->{
            acount.count();
        },"A").start();
        new Thread(()->{
            acount.count();
        },"B").start();

    }
}
class Acount{

    private int num ;
    private Lock lock = new ReentrantLock();

    public void count(){
        lock.lock();
        num++;
        System.out.println(Thread.currentThread().getName()+"第几位"+num+"访客");
        lock.unlock();
    }


}
重入锁

JUC
Java并发编程工具包,Java官方提供的一套专门用来处理并发编程的工具集合(接口+类)
并发:单核CPU,多个线程”同时“运行,实际是交替执行,只不过速度太快,看起来同时执行
并行:多核CPU,真正的多个线程同时运行。
重入锁是JUC使用频率非常高的一个类
ReentrantLock
ReentrantLock就是对Synchronized的升级,目的也是为了实现线程同步

  • ReentrantLock是一个类,Synchronized是一个关键字
  • ReentranLock是JDK实现,Synchronized是JVM实现
  • synchronized可以自动释放锁,ReentrantLock需要手动释放。
    公平锁和非公平锁的区别
    公平锁:线程同步时,多个线程排队,依次执行
    非公平锁:线程同步时,可以插队

Tips

class  Nums{
    private static Integer num = 0;
    private static Integer id = 0;
    public void count(){
        synchronized (num){

            num++;
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"是当前第"+num+"几位访客");
        }

    }

}

如果锁定num则不能同步,锁定id可以同步
分析如下
synchronized必须锁定唯一的元素才可以实现同步
num的值每次都在变,所以num所指的引用一直在变,所以不是唯一的元素,肯定无法实现同步。
id的值永远不变,所以是唯一的元素,可以实现同步。

Reentranlock


import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class Test2 {
    public static void main(String[] args) {
        Account2 account= new Account2();
        new Thread(()->{
            account.count();
        },"A").start();
        new Thread(()->{
            account.count();
        },"B").start();

    }
}

class Account2{
    private static int num;
    private ReentrantLock reentrantLock = new ReentrantLock();
    public void count(){
        reentrantLock.lock();
        num++;
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"是当前的第"+num+"位访客");
        reentrantLock.unlock();
    }
}
  • Lock上锁和解锁都需要开发者手动完成
  • 可以重复上锁
    ReentrantLock除了可以重入之外,还有一个可以中断的特点,可中断是指某个线程在等待获取锁的过程中可以主动终止线程。
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class Test4 {
    public static void main(String[] args) {
        StopLock stopLock = new StopLock();
        Thread t1 = new Thread(()->{
            stopLock.service();
        },"A");

       Thread t2 = new Thread(()->{
            stopLock.service();
        },"B");
        t1.start();
        t2.start();
        try {
            TimeUnit.SECONDS.sleep(1);
            t2.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class StopLock{
    private ReentrantLock reentrantLock = new ReentrantLock();
    public void service(){
        try{
            reentrantLock.lockInterruptibly();
            System.out.println(Thread.currentThread().getName()+" get lock");
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }catch (InterruptedException e1){
            e1.printStackTrace();
        }finally {
            reentrantLock.unlock();

        }
    }
}

在这里插入图片描述

生产者消费者模式

在一个生产环境中,生产者和消费者在同一时间段内共享同一块缓冲区,生产者负责向缓冲区添加数据,消费者负责取出数据。

  • 容器类
public class Container {
    public Humburger[] array = new Humburger[6];
    public  int index = 0;
    /**
     * 向容器内添加汉堡
     */
    public synchronized void push(Humburger humburger){
        while(index == array.length){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }//
        this.notify();
        array[index] = humburger;
        index++;
        System.out.println("生产了一个汉堡"+humburger);
    }
    /**
     * 从容器中取汉堡
     */
    public synchronized Humburger pop(){
        while(index == 0){
            //当前线程暂停
            //正在访问当前资源的线程暂停
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //唤醒之前暂停的线程
        this.notify();
        index--;
        System.out.println("消费了一个汉堡"+array[index]);
        return array[index];
    }

}
  • 生产者
public class Producer {
    private  Container container = null;
    public Producer(Container container){
        this.container = container;
    }
    public void product(){
        for (int i = 0; i < 30; i++) {
            Humburger humburger = new Humburger(i);
            this.container.push(humburger);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

    }
}
  • 消费者
//消费者
public class Consumer {
    private Container container;
    public  Consumer(Container container){
        this.container = container;
    }

    public void consum(){
        for (int i = 0; i < 30; i++) {
            this.container.pop();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

  • 测试类
public class Test {
    public static void main(String[] args) {
        Container container = new Container();
        Producer producer = new Producer(container);
        Consumer consumer = new Consumer(container);

        new Thread(()->{
            producer.product();
        }).start();
        new Thread(()->{
            consumer.consum();
        }).start();



    }
}

汉堡类


public class Humburger {
    private int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "Humburger{" +
                "id=" + id +
                '}';
    }

    public Humburger(int id) {
        this.id = id;
    }
}
多线程并发卖票
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class Ticket {
    //剩余球票
    private int surpluCount = 15;
    //已售出球票
    private int outCount = 0;

     private ReentrantLock reentrantLock = new ReentrantLock();
    public void sale (){
        while(surpluCount > 0 ){
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //票卖完了

            if (surpluCount == 0){
                return;
            }
            reentrantLock.lock();
            surpluCount--;
            outCount++;
            if (surpluCount == 0){
                System.out.println(Thread.currentThread().getName()+"售出"+outCount+"张票,球票已售罄");
            }else {
                System.out.println(Thread.currentThread().getName()+"售出"+outCount+"张票,还剩 "+surpluCount+"张");
            }
            reentrantLock.unlock();
        }
    }
}

public class Test {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(()->{
            ticket.sale();
        },"A").start();
        new Thread(()->{
            ticket.sale();
        },"B").start();
        new Thread(()->{
            ticket.sale();
        },"C").start();

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值