• 多线程【Thread、线程创建】


    主线程:执行主方法的线程,就叫做主线程

    单线程程序:程序从mani开始从上到下依次运行

    程序从main方法开始运行,JVM运行main方法,会找操作系统
    开辟一条通向cpu的执行路径,cpu可以通过这条路径来执行main方法
    这条路径有一个名字叫主(main)线程

    创建线程方式一继承Thread类
    实现步骤:
    1.创建Thread类的子类
    2.重写Thread类中的run方法,设置线程的任务
    3.创建Thread类的子类对象
    4.调用Thread类中的start方法开启一个新的线程,执行run方法
    使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
    结果是两个线程并发地运行;当前线程(main线程)和另一个线程(执行 run 方法的线程)。
    多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。

    打印的结果出现了随机性:
    开启两个线程,对于cpu就选择权利
    喜欢谁,就执行谁,所以就出现了随机性结果

     1 public class MyThread extends Thread{
     2     /*
     3      * 2.重写Thread类中的run方法,设置线程的任务
     4      * 开启这个线程要干什么事情
     5      */
     6     @Override
     7     public void run() {
     8         for (int i = 0; i < 50; i++) {
     9             System.out.println("run..."+i);
    10         }
    11     }
    12 }
    13     public static void main(String[] args) {
    14         //3.创建Thread类的子类对象
    15         MyThread mt = new MyThread();
    16         //mt.run();//不会开启线程,还是单线程程序
    17         //4.调用Thread类中的start方法开启一个新的线程,执行run方法
    18         mt.start();
    19         
    20         new MyThread().start();
    21         
    22         for (int i = 0; i < 50; i++) {
    23             System.out.println("main..."+i);
    24         }
    25     }

    线程的名称:
    主线程:"main"
    开启的其它线程的名称:"Thread-0","Thread-1"....

    获取线程的名称
    1.Thread类中的方法getName
    String getName() 返回该线程的名称。
    2.Thread类中的静态方法,获取当前正在执行的线程
    static Thread currentThread() 返回对当前正在执行的线程对象的引用。
    设置线程的名称:
    1.Thread类中的方法setName(String name)
    void setName(String name) 改变线程名称,使之与参数 name 相同。
    2.子类添加带参构造,调用父类Thread类的带参构造方法,传递线程的名称,让父类给线程起名字(让父亲给儿子起名字)
    Thread(String name) 分配新的 Thread 对象。


    创建线程方式—实现Runnable接口

    实现步骤:
    1.创建Runnable接口的实现类
    2.重写Runnable接口中的run方法,设置线程任务
    3.创建Runnable接口的实现类对象
    4.创建Thread类对象,构造方法中传入Runnable接口的实现类
    Thread(Runnable target) 分配新的 Thread 对象。
    5.调用Thread类中的方法start,开启线程执行run方法


    实现Runnable的好处
    1.避免了类继承Thread类之后,无法继承其它的类(单继承的局限性)
    2.把设置线程任务,和开启线程进行解耦,增强了扩展性
    实现类的作用:就是设置线程任务
    Thread类的作用:开启线程
    好处:传递不同的实现类,实现类重写的方法不一样,可以调用不同的方法

    线程的匿名内部类使用

    匿名:没有名字
    内部类:写在其他类内部的类(成员位置:成员内部类,局部位置(方法中):局部内部类)

    匿名内部类的格式:
    new 父类/接口(){
    重写父类/接口中的方法;
    };

    多线程的父类:
    Thread
    Runnable

    1  new Thread(){
    2             //重写run方法,设置线程任务
    3             @Override
    4             public void run() {
    5                 for (int i = 0; i < 20; i++) {
    6                     System.out.println(Thread.currentThread().getName()+":"+i);
    7                 }
    8             }
    9         }

    以上一堆代码就是一个创建子类重写父类方法的过程
    相当于: new MyThread().start();

    程序出现了线程安全问题:卖了重复的票和不存在的票

    解决方案:
    第一种方式:可以使用同步代码块

    synchronized(锁对象){
    产生安全问题的代码;
    访问了共享数据的代码;
    }

    注意:
    必须要保证多个线程使用的是同一个锁对象
    //在成员位置上创建一个锁对象(保证唯一)

     1 Object obj = new Object();
     2     
     3     @Override
     4     public void run() {
     5         //让卖票重复执行
     6         while(true){
     7         
     8              * 同步代码块
     9              * 程序会频繁的判断锁,获取锁,释放锁,所以会降低速度
    10              
    11             synchronized (obj) {
    12                 if(ticket>0){
    13                     //为了提高安全问题的概率,让程序睡眠
    14                     try {
    15                         Thread.sleep(10);
    16                     } catch (InterruptedException e) {
    17                         e.printStackTrace();
    18                     }
    19                     //卖票ticket--
    20                     System.out.println(Thread.currentThread().getName()+"...卖第"+ticket--+"张票");
    21                 }
    22             }
    23         }
    24     }

    程序出现了线程安全问题:卖了重复的票和不存在的票

    解决方案:
    第二种方式:同步方法

    使用步骤:
    1.把可能出现安全问题的代码抽取到一个方法中
    2.把方法增加一个关键字synchronized
    修饰符 synchronized 返回值类型 方法名(参数){
    可能出现安全问题的代码;
    访问了共享数据的代码;
    }

    同步方法使用的锁对象是什么?
    使用的就是本类对象new RunnableImpl()-->叫this

    静态的同步方法,使用时什么锁对象?
    使用的是本类对象的class属性(反射)
    RunnableImpl.class

     1 *@Override
     2     public void run() {
     3         //让卖票重复执行
     4         while(true){
     5             payTicket2();
     6         }
     7     }
     8     
     9     
    10      * 静态的同步方法
    11      
    12     public static synchronized void payTicket2(){
    13         synchronized (RunnableImpl.class) {
    14             if(ticket>0){
    15                 //为了提高安全问题的概率,让程序睡眠
    16                 try {
    17                     Thread.sleep(10);
    18                 } catch (InterruptedException e) {
    19                     e.printStackTrace();
    20                 }
    21                 //卖票ticket--
    22                 System.out.println(Thread.currentThread().getName()+"...卖第"+ticket--+"张票");
    23             }
    24         }
    25     }
    26     
    27     
    28     
    29      * 抽取出一个同步方法
    30      * 快捷键:alt+shift+m
    31      
    32     public ynchronized void payTicket1() {
    33         synchronized (this) {
    34             //System.out.println(this);//cn.itcsat.demo10.RunnableImpl@67064
    35             if(ticket>0){
    36                 //为了提高安全问题的概率,让程序睡眠
    37                 try {
    38                     Thread.sleep(10);
    39                 } catch (InterruptedException e) {
    40                     e.printStackTrace();
    41                 }
    42                 //卖票ticket--
    43                 System.out.println(Thread.currentThread().getName()+"...卖第"+ticket--+"张票");
    44             }
    45         }
    46     }

    程序出现了线程安全问题:卖了重复的票和不存在的票
    *
    * 解决方案:
    * 第三种方式:使用Lock接口,JDK1.5之后出现的
    *
    * java.util.concurrent.locks.Lock接口
    * 方法:
    * void lock() 获取锁。
    * void unlock() 释放锁。
    * 接口的实现类:ReentrantLock
    *
    * 实现步骤:
    * 1.在成员位置创建一个Lock接口的实现类对象ReentrantLock
    * 2.在可能出现线程安全问题的代码前,调用lock方法获取锁
    * 3.在可能出现线程安全问题的代码后,调用unlock方法释放锁
    *
    *1.在成员位置创建一个Lock接口的实现类对象ReentrantLock
    Lock l = new ReentrantLock();

     1 @Override
     2     public void run() {
     3         //让卖票重复执行
     4         while(true){
     5             //2.在可能出现线程安全问题的代码前,调用lock方法获取锁
     6             l.lock();
     7                 if(ticket>0){
     8                     //为了提高安全问题的概率,让程序睡眠
     9                     try {
    10                         Thread.sleep(10);
    11                         //卖票ticket--
    12                         System.out.println(Thread.currentThread().getName()+"...卖第"+ticket--+"张票");
    13                     } catch (InterruptedException e) {
    14                         e.printStackTrace();
    15                     }finally {
    16                         //3.在可能出现线程安全问题的代码后,调用unlock方法释放锁
    17                         l.unlock();    
    18                     }
    19                 }
    20         }
  • 相关阅读:
    Mac安装Homebrew的那些事儿
    SpringBoot:如何优雅地处理全局异常?
    JDK8日常开发系列:Consumer详解
    Docker 快速安装Jenkins完美教程 (亲测采坑后详细步骤)
    Linux安装Git-两种方式详细教程)
    Linux安装maven(详细教程)
    Linux安装jdk(详细教程)
    Docker基础概念与安装
    JVM参数及调优
    JDK内置工具命令
  • 原文地址:https://www.cnblogs.com/caigq/p/7049119.html
Copyright © 2020-2023  润新知