day31线程1

1.实现多线程

1.3进程和线程【理解】

  • 进程:是正在运行的程序

    独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位 动态性:进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的 并发性:任何进程都可以同其他进程一起并发执行

  • 线程:是进程中的单个顺序控制流,是一条执行路径

    单线程:一个进程如果只有一条执行路径,则称为单线程程序

    多线程:一个进程如果有多条执行路径,则称为多线程程序

1.4实现多线程方式一:继承Thread类【应用】

  • 方法介绍

    方法名说明
    void run()在线程开启后,此方法将被调用执行
    void start()使此线程开始执行,Java虚拟机会调用run方法()
  • 实现步骤

    • 定义一个类MyThread继承Thread类

    • 在MyThread类中重写run()方法

    • 创建MyThread类的对象

    • 启动线程

     public class MyThread extends Thread {
         @Override
         public void run() {
             for(int i=0; i<100; i++) {
                 System.out.println(i);
             }
         }
     }
     public class MyThreadDemo {
         public static void main(String[] args) {
             MyThread my1 = new MyThread();
     ​
     //        my1.run();
     ​
             //void start() 导致此线程开始执行; Java虚拟机调用此线程的run方法
             my1.start();
         }
     }
  • 两个小问题

    • 为什么要重写run()方法?

      因为run()是用来封装被线程执行的代码

    • run()方法和start()方法的区别?

      run():封装线程执行的代码,直接调用,相当于普通方法的调用

      start():启动线程;然后由JVM调用此线程的run()方法

1.5实现多线程方式二:实现Runnable接口

  • Thread构造方法

    方法名说明
    Thread(Runnable target)分配一个新的Thread对象
    Thread(Runnable target, String name)分配一个新的Thread对象
  • 实现步骤

    • 定义一个类MyRunnable实现Runnable接口

    • 在MyRunnable类中重写run()方法

    • 创建MyRunnable类的对象

    • 创建Thread类的对象,把MyRunnable对象作为构造方法的参数

    • 启动线程

  •  public class MyRunnable implements Runnable {
         @Override
         public void run() {
             for(int i=0; i<100; i++) {
                 System.out.println(Thread.currentThread().getName()+":"+i);
             }
         }
     }
     public class MyRunnableDemo {
         public static void main(String[] args) {
             //创建MyRunnable类的对象
             MyRunnable my = new MyRunnable();
     ​
             //创建Thread类的对象,把MyRunnable对象作为构造方法的参数
             //Thread(Runnable target)
     //        Thread t1 = new Thread(my);
     //        Thread t2 = new Thread(my);
             //Thread(Runnable target, String name)
             Thread t1 = new Thread(my,"坦克");
     ​
             //启动线程
             t1.start();
         }
     }

1.6实现多线程方式三: 实现Callable接口【应用】

  • 方法介绍

    方法名说明
    V call()计算结果,如果无法计算结果,则抛出一个异常
    FutureTask(Callable<V> callable)创建一个 FutureTask,一旦运行就执行给定的 Callable
    V get()如有必要,等待计算完成,然后获取其结果
  • 实现步骤

    • 定义一个类MyCallable实现Callable接口

    • 在MyCallable类中重写call()方法

    • 创建MyCallable类的对象

    • 创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数

    • 创建Thread类的对象,把FutureTask对象作为构造方法的参数

    • 启动线程

    • 再调用get方法,就可以获取线程结束之后的结果。

     public class MyCallable implements Callable<String> {
         @Override
         public String call() throws Exception {
             for (int i = 0; i < 100; i++) {
                 System.out.println("跟女孩表白" + i);
             }
             //返回值就表示线程运行完毕之后的结果
             return "答应";
         }
     }
     public class Demo {
         public static void main(String[] args) throws ExecutionException, InterruptedException {
             //线程开启之后需要执行里面的call方法
             MyCallable mc = new MyCallable();
     ​
             //Thread t1 = new Thread(mc);
     ​
             //可以获取线程执行完毕之后的结果.也可以作为参数传递给Thread对象
             FutureTask<String> ft = new FutureTask<>(mc);
     ​
             //创建线程对象
             Thread t1 = new Thread(ft);
     ​
             String s = ft.get();
             //开启线程
             t1.start();
     ​
             //String s = ft.get();
             System.out.println(s);
         }
     }
  • 三种实现方式的对比

    • 实现Runnable、Callable接口

      • 好处: 扩展性强,实现该接口的同时还可以继承其他的类

      • 缺点: 编程相对复杂,不能直接使用Thread类中的方法

    • 继承Thread类

      • 好处: 编程比较简单,可以直接使用Thread类中的方法

      • 缺点: 可以扩展性较差,不能再继承其他的类

1.7设置和获取线程名称

方法名说明
void setName(String name)将此线程的名称更改为等于参数name
String getName()返回此线程的名称
Thread currentThread()返回对当前正在执行的线程对象的引用
 public class MyThreadDemo {
     public static void main(String[] args) {
         MyThread my1 = new MyThread();
 ​
         //void setName(String name):将此线程的名称更改为等于参数 name
         my1.setName("高铁");
 ​
         //Thread(String name)
         MyThread my1 = new MyThread("高铁");
 ​
         my1.start();
 ​
         //static Thread currentThread() 返回对当前正在执行的线程对象的引用
   System.out.println(Thread.currentThread().getName());
     }
 }

1.8线程休眠

方法名说明
static void sleep(long millis)使当前正在执行的线程停留(暂停执行)指定的毫秒数
 public class MyRunnable implements Runnable {
     @Override
     public void run() {
         for (int i = 0; i < 100; i++) {
             try {
                 Thread.sleep(100);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
             System.out.println(Thread.currentThread().getName() + "---" + i);
         }
     }
 }

1.9线程优先级

方法名说明
final int getPriority()返回此线程的优先级
final void setPriority(int newPriority)更改此线程的优先级线程默认优先级是5;线程优先级的范围是:1-10
 public class MyCallable implements Callable<String> {
     @Override
     public String call() throws Exception {
         for (int i = 0; i < 100; i++) {
             System.out.println(Thread.currentThread().getName() + "---" + i);
         }
         return "线程执行完毕了";
     }
 }
 public class Demo {
     public static void main(String[] args) {
         //优先级: 1 - 10 默认值:5
         MyCallable mc = new MyCallable();
 ​
         FutureTask<String> ft = new FutureTask<>(mc);
 ​
         Thread t1 = new Thread(ft);
         t1.setName("飞机");
         t1.setPriority(10);
         //System.out.println(t1.getPriority());//5
         t1.start();
 ​
         MyCallable mc2 = new MyCallable();
 ​
         FutureTask<String> ft2 = new FutureTask<>(mc2);
 ​
         Thread t2 = new Thread(ft2);
         t2.setName("坦克");
         t2.setPriority(1);
         //System.out.println(t2.getPriority());//5
         t2.start();
     }
 }

1.10守护线程

  • 相关方法

    方法名说明
    void setDaemon(boolean on)将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出
     
    public class Demo {
         public static void main(String[] args) {
             MyThread1 t1 = new MyThread1();
             MyThread2 t2 = new MyThread2();
     ​
             t1.setName("女神");
             t2.setName("备胎");
     ​
             //把第二个线程设置为守护线程
             //当普通线程执行完之后,那么守护线程也没有继续运行下去的必要了.
             t2.setDaemon(true);
     ​
             t1.start();
             t2.start();
         }
     }

2.线程同步

2.1卖票

2.2卖票案例的问题

  • 卖票出现了问题

    • 相同的票出现了多次

    • 出现了负数的票

  • 问题产生原因

    线程执行的随机性导致的,可能在卖票过程中丢失cpu的执行权,导致出现问题

2.3同步代码块解决数据安全问题【应用】

  • 安全问题出现的条件

    • 是多线程环境

    • 有共享数据

    • 有多条语句操作共享数据

  • 如何解决多线程安全问题呢?

    • 基本思想:让程序没有安全问题的环境

  • 怎么实现呢?

    • 把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可

    • Java提供了同步代码块的方式来解决

  • 同步代码块格式:

     synchronized(任意对象) { 
         多条语句操作共享数据的代码 
     }

    synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁

  • 同步的好处和弊端

    • 好处:解决了多线程的数据安全问题

    • 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率

     public class SellTicket implements Runnable {
         private int tickets = 100;
         private Object obj = new Object();
     ​
         @Override
         public void run() {
             while (true) {
                 synchronized (obj) { // 对可能有安全问题的代码加锁,多个线程必须使用同一把锁
                     //t1进来后,就会把这段代码给锁起来
                     if (tickets > 0) {
                         try {
                             Thread.sleep(100);
                             //t1休息100毫秒
                         } catch (InterruptedException e) {
                             e.printStackTrace();
                         }
                         //窗口1正在出售第100张票
                         System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
                         tickets--; //tickets = 99;
                     }
                 }
                 //t1出来了,这段代码的锁就被释放了
             }
         }
     }

2.4同步方法解决数据安全问题

  • 同步方法的格式

    同步方法:就是把synchronized关键字加到方法上

    修饰符 synchronized 返回值类型 方法名(方法参数) { 
    	方法体;
    }

    同步方法的锁对象是什么呢?

    this

  • 静态同步方法

    同步静态方法:就是把synchronized关键字加到静态方法上

     修饰符 static synchronized 返回值类型 方法名(方法参数) { 
         方法体;
     }

    同步静态方法的锁对象是什么呢?

    类名.class   

     private static synchronized boolean synchronizedMthod() {
             if(ticketCount == 0){
                 return true;
             }else{
                 try {
                     Thread.sleep(10);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 ticketCount--;
                 System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票");
                 return false;
             }
         }
     }
 ### 2.5Lock锁【应用】
 ​
 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
 ​
 Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
 ​
 - ReentrantLock构造方法
 ​
   | 方法名             | 说明                   |
   | --------------- | -------------------- |
   | ReentrantLock() | 创建一个ReentrantLock的实例 |
 ​
 - 加锁解锁方法
 ​
   | 方法名           | 说明   |
   | ------------- | ---- |
   | void lock()   | 获得锁  |
   | void unlock() | 释放锁  |
 public class Ticket implements Runnable {
       //票的数量
       private int ticket = 100;
       private Object obj = new Object();
       private ReentrantLock lock = new ReentrantLock();
 ​
       @Override
       public void run() {
           while (true) {
               //synchronized (obj){//多个线程必须使用同一把锁.
               try {
                   lock.lock();
                   if (ticket <= 0) {
                       //卖完了
                       break;
                   } else {
                       Thread.sleep(100);
                       ticket--;
                       System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
                   }
               } catch (InterruptedException e) {
                   e.printStackTrace();
               } finally {
                   lock.unlock();
               }
               // }
           }
       }
   }

2.6死锁【理解】

  • 概述

    线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行

  • 什么情况下会产生死锁

    1. 资源有限

    2. 同步嵌套

3.生产者消费者

3.1生产者和消费者模式概述【应用】

所谓生产者消费者问题,实际上主要是包含了两类线程:

一类是生产者线程用于生产数据

一类是消费者线程用于消费数据

  • Object类的等待和唤醒方法

    方法名说明
    void wait()导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法
    void notify()唤醒正在等待对象监视器的单个线程
    void notifyAll()唤醒正在等待对象监视器的所有线程

3.4阻塞队列基本使用【理解】;

  • 常见BlockingQueue:

    ArrayBlockingQueue: 底层是数组,有界

    LinkedBlockingQueue: 底层是链表,无界.但不是真正的无界,最大为int的最大值

  • BlockingQueue的核心方法:

    put(anObject): 将参数放入队列,如果放不进去会阻塞

    take(): 取出第一个数据,取不到会阻塞

     public class Demo02 {
         public static void main(String[] args) throws Exception {
             // 创建阻塞队列的对象,容量为 1
             ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(1);
             // 存储元素
             arrayBlockingQueue.put("汉堡包");
             // 取元素
             System.out.println(arrayBlockingQueue.take());     System.out.println(arrayBlockingQueue.take()); // 取不到会阻塞
         }
     }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值