• 多线程


    程序:一段静态的代码

    进程:正在运行的一个程序,系统在运行时为每一个进程分配不同的内存区域。

    线程:进程的进一步细化,与进程共享一个内存区域,但是有独立的运行栈和程序计数器。

    单核CPU:假的多线程,一个线程执行一次,时间间隔较短,类似多车道,一个收费站。

    多核CPU:更好的发挥多线程的效率。

    一个java程序至少有三个线程:main()、gc()垃圾回收、异常处理线程。

    并行:多个CPU执行多个任务

    并发:一个CPU执行多个任务,如:秒杀,多人做同一件事

    多线程的优点:

    1. 提高程序的相应,对于图形化界面,提高用户的体验
    2. 提高cpu的利用率。
    3. 将程序拆分成多个任务,方便代码的独立运行。

    1、线程的创建和使用

    线程的创建

    方式一:继承于Thread

    1. 创建一个继承Thread类的子类
    2. 重写run()方法
    3. 创建Thread类的子类
    4. 通过子类对象调用start()方法

    start()方法:①启动该线程 ②调用run()方法

    注意

    1. 直接调用run()方法并不会开启新的线程,是主线程正常运行run()方法。
    2. 不可以两次运行(同一对象调用2次start方法)相同的线程,必须重新new 线程对象。

    方式二:实现Runnable接口

    1. 创建一个实现Runnable接口的类。
    2. 实现类实现run()方法。
    3. 创建实现类对象
    4. new Thread(实现类对象)
    5. 通过Thread类的对象调用 start()

    Thread中的常用方法:

    1. start(),启用线程,调用run()
    2. run(),将需要多线程需要执行的方法写里面
    3. currentThread(),静态方法,返回当前执行代码的线程
    4. getName(),获取线程名
    5. setName(),设置线程名 / new Thread("线程名");
    6. yield(),释放当前cpu执行权
    7. join():在线程A中调用线程B的join方法,此时A就进入阻塞状态,直到B执行完,A才继续执行。
    8. sleep(long millitime):让当前线程睡眠一定的毫秒时间
    9. isAlive():判断当前线程是否存活

    线程的调度

    • 线程的优先级:

    MAX_PRIORITY :10

    NORM_PRIORITY :5 默认优先级

    MIN_PRIORITY :1

    注意:高优先级的线程要抢占低优先级的线程cpu执行权,但是只是从概率上讲,并不是一定。

    • 获取或设置当前线程的优先级

    getPriority(): 获取

    setPriority();设置

    比较两种创建线程的方式:

    开发中,优先选择实现Runnable接口的方式。

    原因:

    1. 实现的方式没有类单继承的局限性
    2. 实现的方式更适合处理多个线程共有数据的情况

    2、线程的生命周期

    1. 新建:调用start()
    2. 就绪:等待取得cpu资源
    3. 运行:
    4. 阻塞:①sleep() ②join() ③等待同步锁 ④wait()
    5. 死亡:①执行完 ②stop() ③error/exception且没有处理。

    3、线程的同步

    在java中,我们通过同步机制,来解决线程的安全问题。

    同步的方式:

    好处:解决了线程的安全问题

    局限性:操作同步代码时,只有一个线程能操作,效率比较低。

    方式一:同步代码块

        synchronized(同步监视器){
        //需要被同步的代码
        }
    

    说明:

    1. 需要操作共享数据的,就视为同步的代码。
    2. 同步监视器(锁),任何一个类的对象都可以充当锁。要求:多个线程共用一把锁
    3. 在实现Runnable接口的方式中,可以使用this充当锁
    4. 在继承Thread的方式中,慎用this充当锁。可考虑当前对象

    方式二:同步方法

    操作共享数据的代码完整声明在一个方法中,可使用同步方法的方式。

    实现Runnable接口的方式

    public synchronize void  a(){
    }
    

    继承Thread的方式

    public static synchronize void  a(){
    }
    

    注意:

    1. 在该方式中,没有显式的锁。
    2. 非静态的同步方法,默认锁是this。
    3. 静态的同步方法,琐是当前类本身。

    改写单例模式中的懒汉式为线程安全:

    class Bank{
        private Bank(){}
        private static Bank instance = null;
    
        private static Bank getInstance(){
            
            if (instance == null){
                synchronized (Bank.class){
                    if (instance == null){
                        instance = new Bank();
                    }
                } 
            }
            return instance;
        }
    }
    

    4、线程的死锁

    理解:不同线程占用对方需要的资源不释放,处于僵持的状态,都在互相等待。

    说明:出现死锁后程序并不会出现异常,只是所有线程都处于阻塞的状态。

    解决方法:

    1. 专门的算法、原则
    2. 尽量减少同步资源的定义
    3. 避免嵌套同步

    5、线程的同步--锁

    Lock锁-----jdk5.0新增

    1. 实例化 ReentrantLock()对象

      ReentrantLock lock = new ReentrantLock();
      
    2. 调用锁定方法lock.lock();

    3. 解锁lock.unlock();

    synchronize 与 Lock 异同? (面试题)

    • 相同:都可以解决线程安全问题

    • 不同:synchronize 机制在执行完相应的同步代码后,自动释放锁。

      ​ Lock需要手动上锁(lock())和解锁(unLock());

    使用顺序(建议/非必须)

    Lock-->同步代码块-->同步方法

    如何解决线程安全问题?有几种方式?(面试题)

    三种:同步块,同步方法,锁Lock

    6、线程的通信

    线程通信的三个方法:

    1. wait() : 一旦执行此方法,当前线程就进入阻塞状态,并释放锁
    2. notify() : 执行此方法,唤醒被wait()的线程,如果有多个,就唤醒优先级高的
    3. notifyAll() : 执行此方法,唤醒全部wait()的线程

    注意:

    1. 三个方法只能使用在同步代码块或同步方法中。
    2. 三个方法的调用者,必须是同步代码块或同步方法中的监视器(锁),否者会出现异常。
    3. 三个方法定义在Object中,不是Tread中。

    实例:两个线程交替打印1-100

    public class Test {
    
        public static void main(String[] args) {
            Number number = new Number();
    
            Thread t1 = new Thread(number);
            Thread t2 = new Thread(number);
    
            t1.setName("线程1");
            t2.setName("线程2");
    
            t1.start();
            t2.start();
        }
    }
    
    
    class Number implements Runnable{
        int number = 1 ;
    
        @Override
        public void run() {
            while (true){
                synchronized (this) {
                    notify();
                    if (number<=100){
                        System.out.println(Thread.currentThread().getName()+":"+number);
                        number++;
                    }
                    else{
                        break;
                    }
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
    
        }
    }
    

    sleep()和 wait()方法的异同? (面试题)

    • 相同:都可以使线程阻塞
    • 不同:
    1. 两者声明的位置不同,sleep()声明在Thread类,而wait()声明在Object类。
    2. sleep可以在任何位置使用,而wait()只能在同步代码块和同步方法中使用。
    3. 当两者都使用在同步代码块和同步方法中时,sleep()不会释放锁,wait()会释放锁

    例题生产者消费者

    产品小于0个不能购买,大于0个可购买。

    产品大于20不能再生产,不足20继续生产。

    销售类

    class Clerk{
    
        private int num = 0;
    
        public synchronized void produceProduct() {
            if(num<20){
                num++;
                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+":开始生产第"+num+"个产品");
                notify();
            }else{
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public synchronized void customerProduct() {
            if(num>0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+":开始消费第"+num+"个产品");
                num--;
                notify();
            }else{
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    生产者

    class Producter extends Thread{
    
        private Clerk clerk;
    
        public Producter(Clerk clerk) {
            this.clerk = clerk;
        }
    
        @Override
        public void run() {
            while (true){
                clerk.produceProduct();
            }
        }
    }
    

    消费者

    class Customer extends Thread{
    
        private Clerk clerk;
    
        public Customer(Clerk clerk) {
            this.clerk = clerk;
        }
    
        @Override
        public void run() {
            while (true){
                clerk.customerProduct();
            }
        }
    }
    

    测试

    public class ProductTest {
    
        public static void main(String[] args) {
            Clerk clerk = new Clerk();
    
            Producter p1 = new Producter(clerk);
            Customer c1 = new Customer(clerk);
            Customer c2 = new Customer(clerk);
    
            p1.setName("生产者1");
            c1.setName("消费者1");
            c2.setName("消费者2");
    
            p1.start();
            c1.start();
            c2.start();
        }
    }
    

    7、新增线程的创建方式

    方式一:实现Callable接口

    JDK5.0 新增

    与实现runnable接口相比更加强大:

    1. call方法中可以有返回值。
    2. call()可以抛出异常,被外面的操作捕获。
    3. Callable支持泛型。

    步骤:

    1. 创建Callable的实现类
    2. 将要执行的操作写在Callable实现类的call方法中。
    3. 创建callable的实现类对象
    4. 创建FutureTask 对象 ,callable的实现类对象作为参数。
    5. new Thread(FutureTask 对象).start()开启线程;
    6. 可使用FutureTask 对象的get()方法,获取call()方法的返回值。

    方式二:使用线程池

    思路:提前建立好多个线程,放入线程池,随用随取。

    好处:①提高响应速度②降低资源消耗

    ③便于线程管理:

    1. 线程池大小
    2. 线程数
    3. 没有任务的持续时间

    步骤:

     //1.提供指定数量的线程池
            ExecutorService executorService = Executors.newFixedThreadPool(10);
            //2.执行指定线程操作,需要提供实现runnable/callable类的对象
            executorService.execute(new Number());
    		//executorService.submit(callable callable);
            //3.关闭线程池
            executorService.shutdown();
    

    创建多线程有几种方式?(面试题)

    4种:继承Tread

    实现Runnable

    实现Callable

    线程池

  • 相关阅读:
    Hadoop-HA配置详细步骤
    Oracle 事务
    2 Oracle用户和表空间
    oracle 客户端监听文件配置
    Django 关联管理器
    第2讲 | 网络分层的真实含义是什么?
    hihoCoder #1151 : 骨牌覆盖问题·二 (矩阵快速幂,DP)
    poj 2411 Mondriaan's Dream (轮廓线DP)
    SPOJ COT2 Count on a tree II (树上莫队,倍增算法求LCA)
    HYSBZ 1086 王室联邦 (树的分块)
  • 原文地址:https://www.cnblogs.com/xiaolaodi1999/p/13385062.html
Copyright © 2020-2023  润新知