• java自学之路十五(多线程)


    多线程

    并发:交替执行

    并行:同时进行

    小贴士:command + shift + f7 高亮所有相同变量

    public class DemoMultiThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println("run"+i);
            }
        }
    }
    
        public static void main(String[] args) {
            DemoMultiThread mt = new DemoMultiThread();
            mt.start();
    
            for (int i = 0; i < 20; i++) {
                System.out.println("main" + i);
            }
        }
    

    执行原理

    1. main方法压栈执行
    2. run方法是单线程,start方法会开辟一个新的栈空间
    3. 多个线程之间互不影响,在不同的栈空间

    Thread

    获取线程的名称

    1. Thread类中的getName()
    2. 可以获取当前正在执行的线程,使用线程中getName()获取线程名称
    public class DemoGetName extends Thread{
        @Override
        public void run() {
            String thread_name = getName();
            System.out.println(thread_name);
        }
    }
    
        private static void show02() {
            DemoGetName mt = new DemoGetName();
            mt.start(); //第一个线程
            new DemoGetName().start(); //第二个线程
        }
    
            Thread t = Thread.currentThread();
            System.out.println(t);
            String name = t.getName();
            System.out.println(name);
    

    设置线程的名称

    1. setName(名字)
    2. 构造一个无参和有参方法,把线程名字传递给父类,让父类给子线程起名字

    sleep方法

            for (int i = 0; i < 60; i++) {
                System.out.println(i);
                Thread.sleep(1000);
            }
    

    静态方法,传递的是一个毫秒值

    创建多线程方式二

    实现Runnable接口的类,该类然后实现run方法。Thread构造方法中可以传递该类,start开启线程

    1. 创建Runable接口的实现类

      public class DemoRunable implements Runnable {
          @Override
          public void run() {
              for (int i = 0; i < 10; i++) {
                  System.out.println("i");
              }
          }
      }
      
    2. 在实现类中重写run方法

    3. 创建runable接口的实现类对象

    4. 创建thread类对象,传递runable接口的实现类对象

    5. 调用thread中的start方法

    6.     private static void show04() {
              DemoRunable run = new DemoRunable();
              new Thread(run).start();
          }
      
    
    #### 继承Thread类和Runnable接口区别
    
    - Runnable接口创建多线程的好处
    1. 避免了单继承的局限性
    2. 增强了程序的扩展性,降低了耦合性
    
    #### 匿名内部类创建多线程
    
    
    private static void show05() {
        new Thread(new Runnable(){
            @Override
            public void run() {
                for (int i = 0; i < 30; i++) {
                    System.out.println("hello"+i);
                }
            }
        }).start();
    }
    
    
    #### 同步代码块
    
    

    syncronized(锁对象){
    可能会出现线程安全问题的代码
    }

    
    注意
    
    1. 锁对象可以是任意的对象
    2. 多线程的锁对象是同一个
    
    

    private static void show06() {
    DemoTicket ticket = new DemoTicket();
    Thread mt1 = new Thread(ticket);
    Thread mt2 = new Thread(ticket);
    Thread mt3 = new Thread(ticket);

    mt1.start();
    mt2.start();
    mt3.start();
    

    }

    
    

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

    @Override
    public void run() {
    
    
        while(true){
            synchronized (obj){
                if(ticket>0){
    

    /* 展现bug

                    try{
                        Thread.sleep(1000);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }*/
    
                    System.out.println(Thread.currentThread().getName()+"-->正在卖"+ticket+"张");
                    ticket --;
                }else{
                    break;
                }
            }
        }
    }
    

    }

    
    原理:
    
    使用了一个锁对象,这个锁对象叫同步锁,也叫同步监视器
    
    3个线程一起抢夺cpu执行权,如果进程1先抢到,执行run方法,遇到sychronized代码块这时进程1会检查同步代码块是否有锁对象,如果有,就会获取到锁对象,进入到同步中执行。
    
    进程2抢到了cpu执行权,执行run方法,遇到synchronized代码块,会检查同步代码块是否有锁对象,发现没有就会进入阻塞状态,会一直等待进程1归还锁对象。
    
    同步中的线程,没有执行完毕不会释放锁,同步外的线程没有锁进不去同步
    
    #### 同步方法
    
    1. 把访问了共享数据的代码抽取出来,放到一个方法中
    2. 在方法上添加synchronized修饰符
    
    

    修饰符 synchrond 返回值类型 方法名(params){}

    
    

    public class DemoSellTicket implements Runnable{
    private int ticket = 100;

    @Override
    public void run() {
        while (true){
            sellTicket();
        }
    }
    
    public synchronized void sellTicket(){
        if(ticket>0){
            System.out.println(Thread.currentThread().getName()+"-->正在卖"+ticket+"张");
            ticket --;
        }else{
            return;
        }
    }
    

    }

    
    同步方法也会将方法内部的代码锁住,只让一个线程执行,锁对象就是实现类对象,也就是this
    
    静态同步方法
    
    静态方法 访问 静态变量
    
    

    public class DemoSellTicket implements Runnable{
    private static int ticket = 100;

    @Override
    public void run() {
        while (true){
            sellTicket();
        }
    }
    
    public static synchronized void sellTicket(){
        if(ticket>0){
            System.out.println(Thread.currentThread().getName()+"-->正在卖"+ticket+"张");
            ticket --;
        }else{
            return;
        }
    }
    

    }

    
    **this是创建对象之后产生的,静态方法优先于对象,静态方法的锁对象是本类的class属性**
    
    相当于synchronized(RunnableImpl.class){}
    
    #### Lock锁
    
    1. 在成员位置创建一个reentrantlock对象
    
    2. 在可能出现安全问题的代码钱使用Lock接口类中的lock获取锁
    
    3. 释放锁
    
    

    @Override
    public void run() {
    while (true){
    lock.lock();
    if(ticket>0){
    System.out.println(Thread.currentThread().getName()+"-->正在卖"+ticket+"张");
    ticket --;
    }else{
    lock.unlock();
    break;
    }
    lock.unlock();
    }

    }

    
    释放锁最好配合finally一起使用
    
    #### 等待唤醒机制
    
    多线程间的一种协作机制,一个线程进行了规定操作后,就进入等待状态wait,等待其他线程执行完他们的指定代码后,再将其唤醒notify,再有多个线程进行等待时,如果需要可以使用notifyall来唤醒所有的等待线程。
    
    注意:被通知的等待线程,不能立即恢复执行,因为它当初中断的地方是在同步块内,而此刻它已经不持有锁,所以需要重新去获取锁,才能在调用wait方法之后的地方恢复执行。
    
    - 如果可以获取锁,线程就从waiting编程runnable
    - 否则从wait set出来,进入entry set,线程从waitting变成blocked
    
    1. wait和notify方法必须要由同一个锁对象调用。
    2. wait方法和notify方法是属于object类的方法。
    3. wait方法和notify方法必须要在同步代码块或者同步函数中使用
    
    

    public class ProduceNood implements Runnable {

    Noodles noodles = new Noodles();
    
    public ProduceNood(Noodles noodles) {
        this.noodles = noodles;
    }
    
    @Override
    public void run() {
        synchronized (noodles){
    
                while (true){
                    System.out.println("start");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("end");
                    noodles.notify();
                    noodles.setStatus(true);
                    if(noodles.isStatus()){
                        try {
                            noodles.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
        }
    }
    

    }

    
    

    public class ConsumerNood implements Runnable {
    Noodles noodles = new Noodles();

    public ConsumerNood(Noodles noodles) {
        this.noodles = noodles;
    }
    
    @Override
    public void run() {
        synchronized (noodles){
            while (true){
                    System.out.println("consuming");
                    noodles.setStatus(false);
                    noodles.notify();
                    if(!noodles.isStatus()){
                        try {
                            noodles.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
            }
        }
    }
    

    }

    
    

    public class DemoTest {
    public static void main(String[] args) {
    Noodles noodles = new Noodles();
    noodles.setStatus(false);

        ProduceNood mt1 = new ProduceNood(noodles);
        ConsumerNood mt2 = new ConsumerNood(noodles);
    
        new Thread(mt1).start();
        new Thread(mt2).start();
    
    }
    

    }

    
    #### 线程池
    
    static ExecutorService newFixedThreadPool(int nThreads)
    
    参数 创建线程池中包含线程数量
    
    返回值: executorService接口,返回的是ExecutorService接口的实现类对象,可以用ExecutorService接收(面向接口编程)
    
    用来从线程池中获取线程调用start方法,执行线程任务,submit(runnable task)提交一个runnable任务用于执行,void shutdown() 关闭
    
    步骤
    
    1. 使用线程池工厂类Excutors提供静态方法newFixedThreadPool生产一个指定线程数量的线程池
    2. 创建一个类,实现runnable接口,重写run方法,设置线程任务
    3. 调用ExecutorService中的submit,传递线程任务(实现类),开启线程,执行run
    4. 调用ExecutorService的shutdown销毁线程池(不建议执行)
    
    

    public class DemoThreadPool implements Runnable {
    @Override
    public void run() {
    System.out.println(Thread.currentThread().getName() + "创建了线程池");
    }
    }

    
    

    private static void show01() {
    ExecutorService es = Executors.newFixedThreadPool(2);
    es.submit(new DemoThreadPool());
    es.submit(new DemoThreadPool());
    es.submit(new DemoThreadPool());
    }

  • 相关阅读:
    UOJ 【UR #5】怎样跑得更快
    【TJOJIHEOI2016】求和
    CF 932E Team Work
    【BZOJ2159】Crash的文明世界
    Luogu P4707 重返现世
    Luogu P3175 [HAOI2015]按位或
    【BZOJ3930】选数
    nginx 学习
    如何解决 react-create-app 里面的 no-unused-vars ?
    随时更新web html 项目页面,查看手机等其他移动设备的几种方法?
  • 原文地址:https://www.cnblogs.com/jimmyhe/p/11967727.html
Copyright © 2020-2023  润新知