多线程技术概述

1.线程与进程

  • 进程:内存中运行的应用程序,每个进程都拥有一个独立的内存空间。
  • 线程:是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换、并发执行,一个进程最少有一个线程
  • 线程实际数是在进程基础之上的进一步划分,一个进程启动之后,进程之中的若干执行路劲又可以划分成若干个线程

2.线程的调度

  • 分时调度:所有线程轮流使用CPU的使用权,平均分配时间
  • 抢占式调度:让优先级高的线程先使用,如果优先级相同,则随机选择,Java为抢占式调度,**多线程不能提高程序的运行速度,但能够提高程序运行效率。

3.同步及异步

  • 同步:排队进行,效率低但安全
  • 异步:同时执行,效率高但数据不安全

4.并发与并行

并发:两个或多个事件在同一时间段内发生。

并行:两个或多个事件同一时刻发生

Java中实现多线程

1.继承Thread

继承Thread类之后,需要重写run()方法,并在run(){中写需要执行的代码},在使用时需要初始化类,同时调用类的start()方法。在实现Thread时,可以直接在main方法中直接new Thread(){继承方法}.start()执行。使用.setPriority()设置线程优先级。使用sleep()(静态方法,可直接调用Thread.sleep())休眠/阻塞(消耗时间的操作)线程。
public class MyThread extends Thread {
    @Override
    public void run() {
       //执行的代码块
        for (int i = 0; i <10 ; i++) {
            System.out.println("多线程"+i);
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        /*new Thread(){
            @Override
            public void run(){}
        }.start();*/
        MyThread myThread=new MyThread();
        myThread.start();
        for (int i = 0; i <10 ; i++) {
            System.out.println("线程"+i);
        }
    }
}

执行结果如下:由于是抢占式运行,故运行结果有可能不同。

Java中的多线程怎么用 java中多线程的使用_ide

2.使用Runnable类

与Thread类使用方法一致,但Runnable为接口;且Runnable为任务,需要使用Thread类(线程)执行任务。
/**
     * 用于给线程进行执行的任务
     */
    public class MyRunnable implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i <5 ; i++) {
                System.out.println("Runnable多线程"+i);
            }
        }
    }
    
    public class Demo {
        public static void main(String[] args) {
            //创建任务对象
        
            MyRunnable myRunnable = new MyRunnable();
            //创建线程用于执行任务
            Thread thread = new Thread(myRunnable);
            //执行任务
            thread.start();
            for (int i = 0; i < 5; i++) {
                System.out.println("Runnable多线程" + i);
            }
        }
    }

效果如下:

Java中的多线程怎么用 java中多线程的使用_System_02

3.Runnable比之Thread的优势

1.Runnable通过创建任务,分配个线程后执行,更适合多线程同时执行相同任务的情况。

2.可以避免单继承带来的局限性。

3.任务与线程分离,提高程序的健壮性。

4.线程池技术只接受Runnable类型的任务,不接受Thread类型的线程。

4.线程的中断

一个线程是一个独立的执行路径,应该由其本身决定,若需要中断线程,需要作出标记-》使用Thread.interrupt()方法标记,由于Runnable无法抛出异常,需要用try{}catch(){}抓取异常。
public class StopThread {
        public static void main(String[] args) throws InterruptedException {
            Thread thread=new Thread(new StopRunnable());
            thread.start();
            for (int i = 0; i <5 ; i++) {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
            //对线程进行标记,发现后,进入异常,提醒线程该结束了
            thread.interrupt();
        }
        static class StopRunnable implements Runnable{
    
            @Override
            public void run() {
                try {
                    for (int i = 0; i < 10; i++) {
                        Thread.sleep(1000);
                        System.out.println(Thread.currentThread().getName()+"i");
                    }
    
                } catch (InterruptedException e) {
                    System.out.println("发现标记,结束线程");
                    //结束线程
                    return;
                }
            }
        }
    }

Java中的多线程怎么用 java中多线程的使用_Java中的多线程怎么用_03

5.守护线程及用户线程

  • 用户线程:当进程不包含任何存活的用户线程是,进行结束
  • 守护线程:守护用户线程,当最后一个用户线程结束时,守护线程自动结束。
public static void main(String[] args) throws InterruptedException {
            Thread thread=new Thread(new StopRunnable());
            //设置线程为守护线程
            thread.setDaemon(true );
            thread.start();
            for (int i = 0; i <5 ; i++) {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
        static class StopRunnable implements Runnable{
    
            @Override
            public void run() {
                try {
                    for (int i = 0; i < 10; i++) {
                        Thread.sleep(1000);
                        System.out.println(Thread.currentThread().getName()+"i");
                    }
                } catch (InterruptedException e) {
              
                }
            }
        }
    }

Java中的多线程怎么用 java中多线程的使用_ide_04

6.线程安全问题

线程不安全运行时,可能导致多线程同时进入任务,导致任务未按要求进行
public class TicketDemo {
      public static void main(String[] args) {
          Runnable runnable=new Ticket();
          new Thread(runnable).start();
          new Thread(runnable).start();
          new Thread(runnable).start();
      }
      static class Ticket implements Runnable{
          private int count=10;
          @Override
          public void run() {
              while (count>0){
                  System.out.println("开始卖票");
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  count--;
                  System.out.println("余票"+count);
              }
          }
      }
  }

Java中的多线程怎么用 java中多线程的使用_Java中的多线程怎么用_05

线程安全运行:
  1. 同步代码块(隐式锁),排队执行,使用synchronized(“锁对象”){}锁对象必须是同一个
public class TicketDemo {
       public static void main(String[] args) {
           Runnable runnable = new Ticket();
           new Thread(runnable).start();
           new Thread(runnable).start();
           new Thread(runnable).start();
       }
   
       static class Ticket implements Runnable {
           private int count = 10;
           //锁对象
           private Object o=new Object();
           @Override
           public void run() {
               while (true) {
                   synchronized (o) {
                       if (count > 0) {
                           System.out.println("开始卖票");
                           try {
                               Thread.sleep(1000);
                           } catch (InterruptedException e) {
                               e.printStackTrace();
                           }
                           count--;
                           System.out.println(Thread.currentThread().getName()+"余票" + count);
                       }else{break;}
                   }
               }
           }
       }
   }

Java中的多线程怎么用 java中多线程的使用_i++_06

  1. 同步方法(隐式锁),在方法类型前加上synchronized、方法内部自动调用this调用对象,静态时必须使用类名.class,
public class TicketDemo {
           public static void main(String[] args) {
               Runnable runnable = new Ticket();
               new Thread(runnable).start();
               new Thread(runnable).start();
               new Thread(runnable).start();
           }
       
           static class Ticket implements Runnable {
               private int count = 10;
               //锁对象
               private Object o = new Object();
       
               @Override
               public void run() {
                   while (true) {
                       boolean sale = sale();
                       if (!sale){
                           break;
                       }
                   }
               }
       
               public synchronized boolean sale() {
                   synchronized (o) {
                       if (count > 0) {
                           System.out.println("开始卖票");
                           try {
                               Thread.sleep(1000);
                           } catch (InterruptedException e) {
                               e.printStackTrace();
                           }
                           count--;
                           System.out.println(Thread.currentThread().getName() + "余票" + count);
                       }
                       return false;
                   }
               }

       }

   }

Java中的多线程怎么用 java中多线程的使用_i++_07


3. 显式锁Lock,需要使用子类ReentrantLock,更适合面向对象

1. 初始化锁:Lock l=new ReentrantLock();

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

  public class Demo2 {
      public static void main(String[] args) {
          Runnable runnable = new Ticket();
          new Thread(runnable).start();
          new Thread(runnable).start();
          new Thread(runnable).start();
      }
	 static class Ticket implements Runnable {
          private int count = 10;
          //创建显式锁
          private Lock lock=new ReentrantLock();
          @Override
          public void run() {
              while (true) {
                  //锁住代码块
                  lock.lock();
                  if (count > 0) {
                      System.out.println("开始卖票");
                      try {
                          Thread.sleep(1000);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      count--;
                      System.out.println(Thread.currentThread().getName() + "余票" + count);
                  }else{
                      break;
                  }
                  //解锁
                  lock.unlock();
              }
          }
      }

Java中的多线程怎么用 java中多线程的使用_i++_08


7.公平锁与非公平锁

  • 公平锁:先来后到,执行程序,在显式锁中,初始化时采用有参构造方法即Lock l=newReentrantLock(“fire”:true);代表公平锁
  • 非公平锁:抢占式执行,上述三种方法均为非公平锁。

8.多线程通信问题

类似于生产者与消费者,当生产者未生产商品时,消费者无法执行下一步操作,同理生产者也是。--》先执行一个线程,执行完毕之后睡眠,当另一个线程执行完毕之后唤醒先前执行的线程后进行睡眠,依次交替;
public class Demo3 {
        public static void main(String[] args) {
            Food food=new Food();
            new Cook(food).start();
            new Waiter(food).start();
        }
    
        static class Cook extends Thread {
            private Food food;
    
            public Cook(Food food) {
                this.food = food;
            }
    
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    if (i % 2 == 0) {
                        food.setNameTaste("鸡蛋", "辣的");
                    } else {
                        food.setNameTaste("面条", "咸的");
                    }
                }
            }
        }
    
        static class Waiter extends Thread {
            private Food food;
    
            public Waiter(Food food) {
                this.food = food;
            }
    
            @Override
            public void run() {
                for (int i = 0; i <10 ; i++) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    food.get();
                }
            }
        }
    
        static class Food {
            private String name;
            private String taste;
            //判断是否生产完毕或者消费完毕
            private boolean flag = true;
    
            private synchronized void setNameTaste(String name, String taste) {
                if (flag) {
                    this.name = name;
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    this.taste = taste;
                    flag = false;
                    //唤醒线程
                    this.notifyAll();
                    try {
                        //线程睡眠
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            public synchronized void get() {
                if (!flag) {
                    System.out.println("服务员端走的菜是" + name + ",味道" + taste);
                    flag = true;
                    this.notifyAll();
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

Java中的多线程怎么用 java中多线程的使用_i++_09

9.线程的状态

  1. NEW–线程被建立,单位启动
  2. Runnable–执行状态
  3. Blocked–被阻塞等待监视器锁定
  4. Waiting–无线等待状态,直到被唤醒
  5. TimedWaiting–指定时间等待
  6. TERMINATED–线程已退出

11.特殊创建线程的方法–带返回值Callable

与Runnable相同点:1.都是接口,2.都可以编写多线程,3.都采用Thread.start()启动线程。

与Runnable不同的:1.Runnable无返回值,Callable可返回执行结果,2.Callable的call()方法允许跑出异常,Runnable的run()方法不允许。

1.编写类实现Callable接口,实现call方法
class XXX implements Callable<T>{
    @Override
    public <T> call() throws Exception{
        return T;
    }
}
2.创建FutureTask对象,并传入编写的Callable类对象
	FutureTask<Integer> f=new FutureTask<>(callable);
3.通过Thread,启动线程
	new Thread(f).start();

12.线程池–降低资源消耗、提高响应速度、提高线程可管理性

线程池是一个容纳多个线程的容器,池中的线程可以反复使用,可以节省大量创建线程对象的时间和资源

13、常见的4中线程池

  1. 缓存线程池
/**
  * 缓存线程池.
  * (长度无限制)
  * 执行流程:
  *   1. 判断线程池是否存在空闲线程
  *   2. 存在则使用
  *   3. 不存在,则创建线程 并放入线程池, 然后使用
  */
 ExecutorService service = Executors.newCachedThreadPool();
 //向线程池中 加入 新的任务
 service.execute(new Runnable() {
   @Override
   public void run() {
     System.out.println("线程的名称:"+Thread.currentThread().getName());
   }
 });
 service.execute(new Runnable() {
   @Override
   public void run() {
     System.out.println("线程的名称:"+Thread.currentThread().getName());
   }
 });
 service.execute(new Runnable() {
   @Override
   public void run() {
     System.out.println("线程的名称:"+Thread.currentThread().getName());
   }
 });

2.定长线程池

/**
  * 定长线程池.
  * (长度是指定的数值)
  * 执行流程:
3. 单线程线程池
4. 周期性任务定长线程池
  *   1. 判断线程池是否存在空闲线程
  *   2. 存在则使用
  *   3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
  *   4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
  */
 ExecutorService service = Executors.newFixedThreadPool(2);
 service.execute(new Runnable() {
   @Override
   public void run() {
     System.out.println("线程的名称:"+Thread.currentThread().getName());
   }
 });
 service.execute(new Runnable() {
   @Override
   public void run() {
     System.out.println("线程的名称:"+Thread.currentThread().getName());
   }
 })

3.单线程线程池

/**
  * 定长线程池.
  * (长度是指定的数值)
  * 执行流程:
3. 单线程线程池
4. 周期性任务定长线程池
  *   1. 判断线程池是否存在空闲线程
  *   2. 存在则使用
  *   3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
  *   4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
  */
 ExecutorService service = Executors.newFixedThreadPool(2);
 service.execute(new Runnable() {
   @Override
   public void run() {
     System.out.println("线程的名称:"+Thread.currentThread().getName());
   }
 });
 service.execute(new Runnable() {
   @Override
   public void run() {
     System.out.println("线程的名称:"+Thread.currentThread().getName());
   }
 })

4.周期性任务定长线程池

public static void main(String[] args) {
 /**
  * 周期任务 定长线程池.
  * 执行流程:
  *   1. 判断线程池是否存在空闲线程
  *   2. 存在则使用
  *   3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
  *   4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
  *
  * 周期性任务执行时:
  *   定时执行, 当某个时机触发时, 自动执行某任务 .
   */
 ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
 /**
  * 定时执行
  * 参数1.  runnable类型的任务
  * 参数2.  时长数字
  * 参数3.  时长数字的单位
  */
 /*service.schedule(new Runnable() {
   @Override
   public void run() {
     System.out.println("定时执行");
   }
 },5,TimeUnit.SECONDS);
 */
 /**
  * 周期执行
  * 参数1.  runnable类型的任务
  * 参数2.  时长数字(延迟执行的时长)
  * 参数3.  周期时长(每次执行的间隔时间)
  * 参数4.  时长数字的单位
  */
 service.scheduleAtFixedRate(new Runnable() {
   @Override
   public void run() {
     System.out.println("周期执行");
   }
 },5,2,TimeUnit.SECONDS);
}

14.Lambda表达式–函数式编程思想

接口必须只有一个抽象方法 

Thread t=new Thread(()->{内容})

public class Demo{

    publci static void main(String[] args){
    	//使用lambda表达式实现方法的调用
        print(int sum(int x,int y)->{
        		return x+y;
    			}100,200);
    }
    public static void print(MyMath m,int x,int y){
        int num=m.sum(x,y);
        System.out.println(num);
    }
    static interface MyMath{
        int sun(int x, int y);
    }
}