• 学习笔记_多线程


    本章要点:

    • 多线程的运行原理
    • 创建多线程的两种方式
    • 能过说出多线程的六种状态
    • 能够解决线程安全问题

    多线程的运行原理

      同一时间内,CPU只能处理1条线程,只有1条线程在工作(执行);多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)。如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象。

    创建多线程的两种方式:

      1. 继承Thread类,调用start()方法

      2. 实现Runnable接口

    代码如下:

      自定义线程类

    /**
     * @author 阿豪
     * @Date 2019-5-27
    * 自定义线程类
    */ public class ThreadDemo_01 extends Thread{ /** * 重写run方法,用于线程执行的操作 */ @Override public void run() { for (int i = 0; i < 20;i++) { System.out.println("自定义线程运行第 " + i + ""); } } }

       测试类

    /**
     * @author 阿豪
     * @Date 2019-5-27
     * 测试类
     */
    public class Test {
        public static void main(String[] args) {
            //创建Thread对象
            Thread thread = new ThreadDemo_01();
            //开启自定义线程
            thread.start();
    
            //定义主线程执行操作
            for (int i = 0; i < 20; i++) {
                System.out.println("主线程运行第" + i + "次");
            }
        }
    }

    线程安全问题

      为什么会出现线程安全问题?

      拿火车站售票举例:

        假设总共100张火车票,总共有3个窗口(即3个线程)共同售票,当其中一个窗口已经卖完最后一张票的时候,其他的两个窗口并不知情,继续售卖,结果就会导致出现问题.所以就是线程不安全的问题

      那么,如何解决线程安全问题呢?

      有三种方法

        1. 同步代码块

        2. 同步方法

        3. lock锁

      1. 同步代码块:

        synchronized(锁对象){  

          //可能会发生错误的代码

        }

    注意:

      锁对象:

        1. 锁对象必须被所有线程所共享,即锁对象是唯一的

        2. 锁对象可以是任意对象

    public class MyThread  implements Runnable {
        //总共有100张火车票
        private int ticket = 100;
    
        /**
         * 卖票的方法
         */
        @Override
        public void run() {
            while (true) {
                synchronized (MyThread.class) {
                    if (ticket > 0) {
                        try {
                            Thread.sleep(100);
                            System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "票");
                            ticket--;
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } else {
                        System.out.println("票卖完了");
                        break;
                    }
                }
            }
        }
    }

      2. 同步方法

        将可能发生线程安全问题的代码封装成一个方法,这个方法要加上synchronized来修饰

    public class MyThread  implements Runnable {
        //总共有100张火车票
        private int ticket = 100;
        boolean flag;
        /**
         * 卖票的方法
         */
        @Override
        public void run() {
            flag = true;
            while (flag) {
                method();
            }
        }
    
        public synchronized void method(){
            synchronized (MyThread.class) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                        System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "票");
                        ticket--;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    System.out.println("票卖完了");
                    flag = false;
                }
            }
        }
    }

      3. 方式三:Lock锁

    public class MyThread implements Runnable {
        //总共有100张火车票
        private int ticket = 100;
        //创建look类的子类ReentrantLock, 然后在可能出现线程安全问题的代码前调用lock()方法
        Lock look = new ReentrantLock();
    
        /**
         * 卖票的方法
         */
        @Override
        public void run() {
            while (true) {
                //在可能发生线程安全问题的代码之前, 调用lock()方法
                look.lock();
                try {
                    if (ticket > 0) {
                        Thread.sleep(100);
                        System.out.println(Thread.currentThread().getName() + "正在出售第" + ticket + "票");
                        ticket--;
                    } else {
                        System.out.println("票卖完了");
                        break;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //释放lock()方法 -- unlock()  放在可能发生线程安全问题的代码后
                    look.unlock();
                }
    
            }
    
        }
    }

    测试类:

    /**
     * @author 阿豪
     * @Date 2019-5-27
     * 测试类
     */
    public class Test {
        public static void main(String[] args) {
            MyThread mt = new MyThread();
            Thread t = new Thread(mt);
            Thread t2 = new Thread(mt);
            Thread t3 = new Thread(mt);
            t.start();
            t2.start();
            t3.start();
        }
    }

    多线程的六种状态:

      1. NEW      : 线程刚被创建, 但是未被启动

      2. RUNNABLE  : 可运行jvm里的状态

      3. BLOCKED    : 阻塞状态

      4. WAITING    : 无线等待

      5. TIMEWAITING : 计时等待

      6. TEMINATED  : 被终止 (因run()方法执行完成, 正常终止)

     
  • 相关阅读:
    exec系列函数和system函数
    fork函数相关总结
    文件的内核结构file和dup实现重定向
    进程基本概述
    fcntl 函数与文件锁
    文件的属性
    目录的操作
    文件的读取写入
    文件的打开关闭
    浅谈原始套接字 SOCK_RAW 的内幕及其应用(port scan, packet sniffer, syn flood, icmp flood)
  • 原文地址:https://www.cnblogs.com/myBlog-ahao/p/10932742.html
Copyright © 2020-2023  润新知