首页 > 编程语言 >关于我学习java的小结09

关于我学习java的小结09

时间:2024-09-24 22:48:47浏览次数:9  
标签:java synchronized Thread 09 死锁 线程 run 小结 public

一、知识点

线程。

二、目标

  1. 理解进程和线程。

  2. 掌握创建多线程的方式。

  3. 理解线程的生命周期。

  4. 掌握死锁。

三、内容分析

  1. 重点

    • 多线程的创建方式。

    • 线程的生命周期。

    • 死锁的形成条件。

  2. 难点

    • 多线程的的理解。

    • 死锁。

四、内容

1、线程

1.1 什么是进程

进程(Process):进程是计算机中的程序关于某数据集合上的一次运行活动,是操作系统进行资源分配与调度的基本单位。

可以简单理解为:进程是正在操作系统中运行的一个程序

1.2 什么是线程

线程(thread):线程是进程的一个执行单元

一个线程就是进程中一个单一顺序的控制流,是进程的一个分支。

进程与线程之间的关系:

进程是线程的容器,一个进程至少有一个线程,一个进程也可以有多个线程。

在操作系统中是以进程为单位分配内存,同一个进程的所有线程共享该进程所有资源。

如果把一个进程看作是一个工厂,线程就是工厂中的若干流水线,若干流水线共享工厂的某些资源。

1.3 创建线程的方式
1.3.1 继承Thread类

继承Thread类,重写run方法。使用start()方法开启线程,调用run()方法和调用普通方法没有区别。

// 没有使用多线程,分别打印两次1~20
for(int i = 1; i <= 20; i++) {
    System.out.println(i);
}
for(int i = 1; i <= 20; i++) {
    System.out.println(i);
}
// 使用多线程,两个for循环交替打印
public class MyThread extends Thread {
​
    @Override
    public void run() {
        for(int i = 1; i <= 20; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
        }
    }
​
    public static void main(String[] args) {
        Thread thread1 = new MyThread();
        Thread thread2 = new MyThread();
​
        thread1.start();
        thread2.start();
    }
​
}

对比效果:当我们没有使用多线程执行两个for循环,结果得按顺序一个for循环执行结束,开始另一个for循环。

当我们使用多线程执行两个for循环,两个for循环同时执行。

1.3.2 实现Runable接口

实现Runable接口,重写run方法。

public class MyThread implements Runnable {
​
    @Override
    public void run() {
        for(int i = 1; i <= 20; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
        }
    }
​
    public static void main(String[] args) {
        Thread thread1 = new Thread(new MyThread());
        Thread thread2 = new Thread(new MyThread());
​
        thread1.start();
        thread2.start();
    }
​
}
1.3.3 实现Callable接口

实现Callable接口,使用线程池创建多个线程。

public class MyThread implements Callable {
​
    @Override
    public Object call() throws Exception {
        for(int i = 1; i <= 20; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
        }
        return null;
    }
​
    public static void main(String[] args) {
        //创建线程
        MyThread myThread = new MyThread();
        
        //ExecutorService类作用是管理线程
        //Executors 是一个工具类,线程池工厂,用来创建不同类型的线程池对象
        ExecutorService pool = Executors.newFixedThreadPool(2);
​
        //submit方法:将执行的任务添加到线程池中
        pool.submit(thread);
        pool.submit(thread);
        
        //关闭线程池服务
        pool.shutdown();
    }
}
1.4 线程原理

线程的并发执行是通过多个线程不断的切换CPU资源,这个速度非常快,我们感知不到,我们能感知的就是几个线程并发在执行。

1.5 线程生命周期

  1. 新建:线程被new出来。

  2. 就绪:线程具有执行的资格,即线程调用了start(),没有执行的权利,就是调用的线程的start()方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行。

  3. 运行:具备执行的资格和执行的权利,当就绪的线程被调用并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能。

  4. 阻塞:没有执行的资格和执行的权利,在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep()、wait()之后,线程就处于阻塞状态,这个时候需要其他机制将处于阻塞状态的线程唤醒,比如调用notify或者notifyAll方法。唤醒的线程不会立刻执行run方法,它们需要再次等待CPU分配资源进入运行状态。

  5. 销毁/死亡:线程释放资源,如果线程正常执行完毕后或线程被提前强制性的终止或者出现异常导致结束,那么线程就要被销毁,释放资源 。

1.6 synchronized

使用多线程提高了效率,但是也带来一个问题,当多个线程共享资源的时候,会出现线程不安全问题。比如几个窗口同时卖100张电影票,这100张电影票是共享的,会造成两个窗口同时读取到同一张票的可能。

synchronized关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

  1. 存在线程安全问题的写法

    // 存在线程安全问题的写法
    public class SellTicket extends Thread {
    ​
        // 使用static保存100张票
        private static int tickets = 100;
    ​
        @Override
        public void run() {
            while (true) {
                if(tickets <= 0) {
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "卖出了第几" + tickets-- + "号票");
                try {
                    Thread.sleep(100); // 模拟延时,买票是需要时间的。
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    ​
        public static void main(String[] args) {
    ​
            Thread thread1 = new SellTicket();
            Thread thread2 = new SellTicket();
    ​
            // 同时开启两个窗口买票,发现结果有重复的票号
            thread1.start();
            thread2.start();
    ​
        }
    }

  2. 使用synchronized修饰方法体

    public class SellTicket extends Thread {
    ​
        private static int tickets = 100;
    ​
        // 创建锁
        private static Object object = new Object();
    ​
        @Override
        public void run() {
            while (true) {
                synchronized (object) {
                    if(tickets <= 0) {
                        break;
                    }
                    System.out.println(Thread.currentThread().getName() + "卖出了第几" + tickets-- + "号票");
                    try {
                        Thread.sleep(100); // 模拟延时,买票是需要时间的。
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    ​
        public static void main(String[] args) {
    ​
            Thread thread1 = new SellTicket();
            Thread thread2 = new SellTicket();
    ​
            thread1.start();
            thread2.start();
        }
    }
  3. 使用synchronized修饰方法

    public class SellTicket extends Thread {
    ​
            private static int tickets = 100;
    ​
            @Override
            public void run() {
            while (true) {
                if(sell()) {
                    break;
                }
            }
        }
    ​
        public synchronized static boolean sell() {
            boolean sign = false;
            if(tickets > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出了第几" + tickets-- + "号票");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                sign = true;
            }
            return sign;
        }
    ​
        public static void main(String[] args) {
    ​
            Thread thread1 = new SellTicket();
            Thread thread2 = new SellTicket();
    ​
            thread1.start();
            thread2.start();
        }
    }
1.7 死锁
1.7.1 定义

当两个或两个以上的线程因竞争相同资源而处于无限期的等待,这样就导致了多个线程的阻塞,出现程序无法正常运行和终止的情况。

假设场景:有一个羽毛球场和一个网球场,小明占用了羽毛球场,小红占用了网球场。小明在打羽毛球的过程中就想如果网球场空闲了,他就去打网球;同时小红也在想,如果羽毛球场空闲了,她就去打羽毛球。但是他们去另一个球场的前提是另一个球场空闲了出来,这时候他们保持着占用现有资源,然后又想获取对象的占用资源,这时候就造成死锁了。

public static void main(String[] args) {
    String str1 = "羽毛球场";
    String str2 = "网球场";
​
    Thread thread1 = new Thread(){
        @Override
        public void run() {
            synchronized (str1) {
                System.out.println("小明占用了" + str1);
​
                // 小明占用羽毛球场的同时,还想去尝试获取网球场
                synchronized (str2) {
                    System.out.println("小明占用了" + str2);
                }
            }
        }
    };
​
    Thread thread2 = new Thread(){
        @Override
        public void run() {
            synchronized (str2) {
                System.out.println("小红占用了" + str2);
​
                // 小红占用网球场的同时,还想去尝试获取羽毛球场
                synchronized (str1) {
                    System.out.println("小红占用了" + str1);
                }
            }
        }
    };
​
    thread1.start();
    thread2.start();
}

运行结果:程序一直处于运行状态,进入了死锁,卡住了。

1.7.2 产生死锁的必要条件
  1. 互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用。即一个资源每次只能被一个进程使用。

  2. 不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。

  3. 请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。

  4. 循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。

1.7.3 修复死锁
  1. 预防死锁:通过设置某些限制条件,去破坏死锁的四个必要条件中的一个或者几个,来进行预防。

  2. 避免多锁:尽量避免使用多个锁,并且只有需要时才持有锁。否则嵌套的synchronized非常容易出现问题。

  3. 设计锁的顺序:如果能确保所有的线程都是按照相同的顺序获取锁,那就不会出现锁问题。

标签:java,synchronized,Thread,09,死锁,线程,run,小结,public
From: https://blog.csdn.net/qq_40508161/article/details/142501979

相关文章

  • java中关于继承的题目1
    编写一个圆类Circle,该类拥有:1)一个成员变量,radius(私有,浮点型);//存放圆的半径;2)两个构造方法Circle() //将半径设为0Circle(double r) //创建Circle对象时将半径初始化为r3)三个成员方法doublegetArea() //获取圆的面积doublegetPerimeter() //获取圆的周长voidshow() //将......
  • javaweb学习2 -2024/9/24
    今天学习了数据库中约束的概念数据库-约束约束的概念约束是作用于表中列上的规则,用于限制加入表的数据约束的存在保证了数据库中数据的正确性,有效性和完整性约束的分类#约束createtableemp2(#自动增长auto_increment当列时数据类型并且唯一约束id......
  • java_day7_继承、final关键字、代码块、多态
    一、继承1、继承我想养一只......
  • java继承中方法的覆盖
    当子类中的方法声明【方法的返回值类型,方法名和参数列表】与父类中的方法声明一致的时候,只是方法的实现不一样,这个现象叫做方法的重写【方法的覆盖】。classFu5{publicvoidfun1(){System.out.println("这是父类中的fun1方法");}}classZi5extendsFu5{......
  • java继承关系中super关键字
    super关键字的使用和注意事项:1、super仅代表当前类中的直接父类,无法使用super获取父类的父类中的成员2、super后面不能再调用superclassGrandFu3{inta=10;}classFu3extendsGrandFu3{inta=30;}classSon3extendsFu3{inta=20;publicv......
  • javascript是什么语言?它是干什么的?
    javascript简称“js”是浏览器端的脚本语言,是用来处理网页客户端与用户的交互的一种行为,以及实现页面特效。它是一种高级、直译式、解释型语言,是一种基本的原型、函数先行语言,它支持面向对象编程、命令式编程、以及函数式编程。 要知道javascript是干什么的,我们首先要......
  • java_day6_this关键字、构造方法、static关键字、工具类、文档注释
    一、this关键字this代表的是调用该方法的当前对象【谁调用了该方法,this就代表那个对象】this:代表的是调用当前方法的对象this可以使用对象中的成员变量,成员方法,构造方法变量查找规则:就近原则先在方法中查找变量,若找到就使用若方法中没有该变量,去成......
  • [JVM] 应用诊断工具之javac命令
    0引言1JDK命令:javac反编译指定类:javac-c{className}参考命令:C:\Users\xxxxxx>javap-cjava.lang.Object>"Object.txt"C:\Users\xxxxxx>javap-cjava.lang.ObjectCompiledfrom"Object.java"publicclassjava.lang.Object{p......
  • java中的继承关系
    继承是指我有多个类,而且这些类都有相同的属性和方法的时候就可以使用继承关系java提供了一个关键字用来表示继承关系:extends写法:classBextendsA{}表示B类继承自A类这里的B称之为:子类,派生类这里的A称之为:父类,基类或者超类子类只能继承父类的成员变量和成员方法,并且不是私......
  • 02.Java流程控制
    1.Scanner对象可以通过Scanner类来获取用户的输入基本语法Scanners=newScanner(System.in);通过Scanner类的next()和nextLine()方法获取输入的字符串,在读取前我们一般需要使用hasNext()和hasNextLine()来判断是否还有输入的数据next()一定要读取到有效字符后才......