• 多线程笔记


    进程和线程

    进程:应用程序的执行实例,有独立的内存空间和系统资源

    线程:CPU调度和分派的基本单位,进程中执行运算的最小单位,可完成一个独立的顺序控制流程

    进程包含线程,一个进程至少有一个线程。

    多线程

    什么是多线程?

    1.如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程”

    2.多个线程交替占用CPU资源,而非真正的并行执行

    多线程好处

    1.充分利用CPU的资源

    2.简化编程模型

    3.带来良好的用户体验

    主线程

    1.main()方法即为主线程入口

    2.产生其他子线程的线程

    3.必须最后完成执行,因为它执行各种关闭动作

    使用线程的步骤

    1.定义线程

    2.创建线程对象

    3.启动线程

    4.终止线程

    在Java中创建线程的两种方式

    Thread类

    Java提供了java.lang.Thread类支持多线程编程

    步骤:

    1.定义MyThread类继承Thread类

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

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

    //第一步:创建MyThread类继承Thread
    public class MyThread extends Thread{
        
        //第二步:重写Thread中的run方法
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName()+"-"+i);
            }
        }
        
    }
    ​
    public class MyThreadTest {
    ​
        public static void main(String[] args) {
            //第三步:创建线程类对象
            MyThread mt1 = new MyThread();
            MyThread mt2 = new MyThread();
            mt1.setName("线程A");
            mt2.setName("线程B");
            //mt1.run( );
            //启动线程要使用strart()方法来启动线程,如果通过线程对象直接调用run()方法,
            //那是由主线程来调用的,不是多现成的实现方式
    ​
            mt1.start();
            mt2.start();
        }
    ​
        /*
        *多线程并不是真正意义上的并行执行,而是多个线程交替性抢占CPU资源,
        *谁获取了CPU资源就执行谁*在执行过程中,有可能丢失CPU资源,
        *被另外一个线程执行,所以会出现交替执行的现象
        *
        ***/
    ​
    }
    ​

    Runnable接口:

    实现java.lang.Runnable接口

    步骤:

    1.定义MyRunnable

    2.实现Runnable接口实现run()方法,编写线程执行体

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

    //1.定义MyRunnable类
    public class MyRunnable implements Runnable{
    //2.实现Runnable接口实现run()方法,编写线程执行体
        @Override
        public void run() {
            
            for(int i =1;i<=10;i++){
            System.out.println( Thread.currentThread( ).getName()+" :"+i);
            }
                    
        }
    ​
        
    ​
    }
    public class MyRunnableTest {
        
        public static void main(String[] args) {
            // 创建线程对象,调用start()方法启动线程
    ​
            MyRunnable mr1 = new MyRunnable();
            // MyRunnable类中没有start()方法,其父类Object类中也没有start()方法,其实现的接口Runnable中只有run()方法,没有start()方法
    ​
            Thread thread1 = new Thread(mr1,"线程A");
            thread1.start();
            Thread thread2 = new Thread(mr1,"线程B");
            thread2.start();
            Thread thread3 = new Thread(mr1,"线程C");
            thread3.start();
        }
    ​
    }

    比较两种创建线程的方式

    继承Thread类

    1.编写简单,可直接操作线程

    2.适用于单继承

    实现Runnable接口

    1.避免单继承局限性

    2.便于共享资源

    线程的状态

     

    线程调度

    线程调度指按照特定机制为多个线程分配CPU的使用权

    说 明
    void setPriority(int newPriority)更改线程的优先级
    static void sleep(long millis)在指定的毫秒数内让当前正在执行的线程休眠
    void join()等待该线程终止
    static void yield()暂停当前正在执行的线程对象,并执行其他线程
    void interrupt()中断线程
    boolean isAlive()测试线程是否处于活动状态

    线程优先级

    线程优先级由1~10表示,1最低,默认优先级为5优先级高的线程获得CPU资源的概率较大

    public static void main(String[] args) {
            MyRunnable mr = new MyRunnable();
            Thread t1= new Thread (mr,"线程A");
            Thread t2 = new Thread ( mr,"线程B");
            //输出两个线程的优先级
            System.out.println(t1.getPriority());
            System.out.println(t2.getPriority());
            
            t1.setPriority (Thread.MAX_PRIORITY);
            t2.setPriority (Thread.MIN_PRIORITY);
            
            // 输出两个线程的优先级
            System.out.println(t1.getPriority());
            System.out.println(t2.getPriority());
            
            t1.start();
            t2.start();
            //线程优先级高只能说明线程获取CPU资源的概率大一些,
            //不能保证每一次优先级高的线程先获取CPU资源
        }

    线程休眠

    让线程暂时睡眠指定时长,线程进入阻塞状态睡眠时间过后线程会再进入可运行状态

    public class MyRunnable implements Runnable {
    ​
        public void run() {
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + ": " + i);
                if (i == 5) {
                    try {
                        System.out.println(Thread.currentThread().getName()+"开始休眠");
                        //线程休眠5s
                        Thread.sleep(5000);
                        System.out.println(Thread.currentThread().getName()+"休眠结束");
                        
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    ​

    线程的强制运行

    使当前线程暂停执行,等待其他线程结束后再继续执行本线程

    1. public final void join()

    1. public final void join(long mills)

    1. public final void join(long mills,int nanos)

    millis:以毫秒为单位的等待时长

    nanos:要等待的附加纳秒时长

    需处理InterruptedException异常

    public class MyRunnableTest {
    ​
        public static void main(String[] args) {
            Thread tr = new Thread(new MyRunnable(), "线程A");
            tr.start();
    ​
            for (int i = 1; i <= 10; i++) {
                if (i == 3) {
                    try {
                        /*
                         * 线程强制执行:tr强制执行,一直等到tr执行完毕之后,
                         * 才会释放CPU资源给其它线程执行,在调用join()方法之前,
                         *  多个线程依然是抢占CPU
                         */
                        tr.join();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
    ​

    线程的礼让

    暂停当前线程,允许其他具有相同优先级的线程获得运行机会该线程处于就绪状态,不转为阻塞状态

    public class MyRunnable implements Runnable {
    ​
        public void run() {
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + ": " + i);
                if(i==3){
                    System.out.print("线程礼让:");
                    /*
                     * 线程礼让:让当前执行run()方法的线程释放CPU资源,
                     * 给其它线程一个获得CPU资源的机会,
                     * 但是当前线程还会抢占CPU资源,也就是说,
                     * 当前线程释放CPU资源后,会与其它线程再一次抢占CPU,
                     * 就看其它线程能不能抓住这个机会
                     */
                    Thread.yield();
                }
            }
        }
    }

    多线程共享数据引发的问题

    public class Site implements Runnable {
        private int count = 10;
        private int num = 0;
    ​
        @Override
        public void run() {
            while (true) {
                if (count <= 0) {
                    break;
                }
                //如果票的数量小于日,就不在卖票
                count--;
                num++;
                //买票过程中模拟网速缓慢
                System.out.println(Thread.currentThread().getName() + "买到了第" + num
                        + "张票,还剩余" + count + "张票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                
                
    ​
            }
        }
    ​
    }
    public class Test {
    ​
        public static void main(String[] args) {
            Site site = new Site( );
            Thread t1 = new Thread( site,"张三");
            Thread t2 = new Thread( site,"李四");
            Thread t3 = new Thread( site,"王五");
            t1.start();
            t2.start();
            t3.start();
    ​
            /*
             * 当多个线程操作共享数据的时候容易出现问题:
             * 在一个线程操作这个共享数据的时候,还没有完全操作完毕,
             * 线程失去了CPU资源,CPU被另外一个线程获取,
             * 另外一个线程会接着上一个线程操作的数据继续进行操作
             * 当另外一个线程操作数据完毕之后,第一个线程又获取了CPU资源,
             * 此时,第一个线程看到的数据是第二个线程操作后的数据,
             * 由此就会引发数据不安全的问题。
             */
        }
    }
    ​
    运行结果
    张三买到了第3张票,还剩余7张票
    李四买到了第3张票,还剩余7张票
    王五买到了第3张票,还剩余7张票
    王五买到了第6张票,还剩余4张票
    张三买到了第6张票,还剩余4张票
    李四买到了第6张票,还剩余4张票
    李四买到了第7张票,还剩余3张票
    王五买到了第7张票,还剩余3张票
    张三买到了第7张票,还剩余3张票
    张三买到了第8张票,还剩余2张票
    李四买到了第8张票,还剩余2张票
    王五买到了第8张票,还剩余2张票
    张三买到了第10张票,还剩余0张票
    王五买到了第10张票,还剩余0张票

    同步方法

    1.使用synchronized修饰的方法控制对类成员变量的访问

    访问修饰符 synchronized 返回类型 方法名(参数列表){……}

    或者

    synchronized 访问修饰符 返回类型 方法名(参数列表){……}

    public class Site implements Runnable {
        private int count = 10;
        private int num = 0;
    ​
        @Override
        public void run() {
            while (true) {
                if(!sale()){
                    break;
                }
            }
        }
        public synchronized boolean sale() {
            if (count <= 0) {
                return false;
            }
            //如果票的数量小于日,就不在卖票
            count--;
            num++;
            //买票过程中模拟网速缓慢
            System.out.println(Thread.currentThread().getName() + "买到了第" + num
                    + "张票,还剩余" + count + "张票");
            try {
                Thread.sleep(1000);
                //this.notifyAll();
                //this.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return true;
        }
    }
    ​
    运行结果:
    ​
    张三买到了第1张票,还剩余9张票
    张三买到了第2张票,还剩余8张票
    张三买到了第3张票,还剩余7张票
    王五买到了第4张票,还剩余6张票
    王五买到了第5张票,还剩余5张票
    王五买到了第6张票,还剩余4张票
    王五买到了第7张票,还剩余3张票
    王五买到了第8张票,还剩余2张票
    王五买到了第9张票,还剩余1张票
    李四买到了第10张票,还剩余0张票

    2使用synchronized关键字修饰的代码块

    public class Site implements Runnable {
        private int count = 10;
        private int num = 0;
    ​
        @Override
        public void run() {
            while (true) {
                synchronized (this) {
                    if (count <= 0) {
                        break;
                    }
                    // 如果票的数量小于日,就不在卖票
                    count--;
                    num++;
                    // 买票过程中模拟网速缓慢
                    System.out.println(Thread.currentThread().getName() + "买到了第"
                            + num + "张票,还剩余" + count + "张票");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
    ​
                }
            }
        }
    ​
    }
    ​
    运行结果:
    ​
    张三买到了第1张票,还剩余9张票
    张三买到了第2张票,还剩余8张票
    张三买到了第3张票,还剩余7张票
    张三买到了第4张票,还剩余6张票
    张三买到了第5张票,还剩余5张票
    张三买到了第6张票,还剩余4张票
    王五买到了第7张票,还剩余3张票
    王五买到了第8张票,还剩余2张票
    李四买到了第9张票,还剩余1张票
    李四买到了第10张票,还剩余0张票
    ​

    线程安全的类型

    ** **方法是否同步效率比较适合场景
    线程安全多线程并发共享资源
    非线程安全单线程

    常见类型对比

    Hashtable && HashMap

    Hashtable

    1.继承关系实现了Map接口,Hashtable继承Dictionary类

    2.线程安全,效率较低

    3.键和值都不允许为null

    HashMap

    1.继承关系实现了Map接口,继承AbstractMap

    2.非线程安全,效率较高

    3.键和值都允许为null

    StringBuffer && StringBuilder

    前者线程安全,后者非线程安全

  • 相关阅读:
    .NET 开源工作流: Slickflow流程引擎高级开发(十) -- BpmnJS流程设计器集成
    Linux命令--重启系统的方法
    CAN bus总线静电保护方案
    17-spark任务划分
    基于php的校园公寓管理系统设计与实现
    Flutter笔记:build方法、构建上下文BuildContext解析
    对象序列化
    gpt_academic的使用——含一键安装和接入其他API以及本地模型
    分享一个项目:go `file_line`,在编译期得到源码行号,减少运行期runtime消耗
    大数据挖掘决策树计算过程
  • 原文地址:https://blog.csdn.net/lmdbhf/article/details/126425005