多线程

本文深入探讨了Java中的多线程概念,包括进程与线程的区别、并发与并行的实现。详细介绍了如何创建线程,如继承Thread类、实现Runnable接口以及使用匿名内部类。同时,讲解了线程的并发安全问题,如可见性、有序性和原子性,并提供了volatile关键字和原子类的解决方案。文章还涵盖了synchronized关键字和Lock锁的使用,以及并发包中的并发容器如CopyOnWriteArrayList、CopyOnWriteArraySet和ConcurrentHashMap的应用。最后,讨论了多线程协作工具,如CountDownLatch、CyclicBarrier和Semaphore,以及线程信息交互的Exchanger。
摘要由CSDN通过智能技术生成

线程

进程与线程_并行与并发的概念

  1. 进程:是“操作系统”的概念,指一个“独立运行的程序”,由操作系统管理。
  2. 线程:是由“进程”创建。指一个“进程”中可以将一段代码分离出来,与“主进程”“同时”执行。这样可以使我们的程序同时做多件事情,提高程序的运行效率。
    其实Java程序本身都是“多线程”的。JVM启动(进程),会加载我们的main()方法,会被JVM以“线程”的方式执行。
    “垃圾回收器(GC)”也会被JVM以“线程”的方式在运行。
  3. 并发:一颗CPU,多个线程;
  4. 并行:两颗CPU,两个线程;

创建线程的方式一_继承Thread类

  1. 自定义线程,继承自Thread,并重写“run()”方法;

  2. 启动线程:

    1. 创建自定义线程对象;
    2. 调用自定义线程对象的“start()”方法,启动线程。
      注意:

    1).一个“线程类”,可以创建多个“线程对象”,每个“线程对象”都可以独立的启动、运行。
    2).每个“线程对象”只能start()一次,运行完毕,会自动成为垃圾。
    3).在“线程”中,重写的是“run()”方法,而启动线程调用的是“start()”方法。

代码演示:
  1. 自定义线程
public class MyThread extends Thread {
   
    @Override
    public void run() {
   
            for (int i = 0; i < 30; i++) {
   
                System.out.println("敌人开枪:" + i);
            }
    }
}

  1. 测试类
public class Demo01 {
   
    public static void main(String[] args) {
   
        //做一个游戏,有两个人物:我、敌人
        //两个人相互开枪,各开30枪

        //启动敌人线程
        MyThread t = new MyThread();
        t.start();

        //启动线程后,主线程会继续向下走
        //我开枪
        for (int i = 0; i < 30; i++) {
   
            System.out.println("我开枪:" + i);

        }
    }
}

创建线程的方式二_实现Runnable接口

步骤:
1. 自定义类,实现Runnable接口,并重写run()方法;
2. 启动线程:
1).创建自定义类对象;
2).创建一个Thread对象,并且要将自定义对象作为实参传给Thread的构造方法;
3).调用Thread对象的start()启动线程。
注意:
可以只创建一个Runnable子类对象,然后传给几个Thread对象,每个Thread对象可以单独启动,独立运行;

代码演示
  1. 自定义类,实现Runnable接口,并重写run():
	public class MyRunnable implements Runnable {
   
    @Override
    public void run() {
   
        for (int i = 0; i < 30; i++) {
   
            System.out.println("敌人开枪:" + i);

        }
    }
}
  1. 测试类:
public class Demo02 {
   
    public static void main(String[] args) {
   
        //1.创建自定义对象
        MyRunnable myRun = new MyRunnable();
        //2.创建Thread对象,将MyRunnable委托给Thread去执行
        Thread t = new Thread(myRun);
        Thread t2 = new Thread(myRun);
        //3.启动线程
        t.start();
        t2.start();

        //启动线程后,主线程继续运行
        for (int i = 0; i < 30; i++) {
   
            System.out.println("我开枪:" + i);

        }
    }
}

继承Thread与实现Runnable两种创建多线程方式的对比

  1. 需要定义线程类,继承自Thread类,而Java是单继承,这样就对子类形成了限制,以后再想继承其他类,就不可以了。
  2. 需要定义类,实现Runnable接口,对子类没有限制,子类仍然可以继承其他类,也可以同时实现多个接口。

匿名内部类的方式实现线程

  1. 匿名内部类的格式:
    new 父类/父接口(){
    //子类类体
    }
  2. 两种使用匿名内部类的格式:
    1. 继承Thread的匿名内部类的形式:
      new Thread( ){
      //子类类体
      }.start();
    2. 实现Runnable接口的匿名内部类的形式:
      new Thread(new Runnable(){
      //子类类体
      }
      ).start();
代码演示:
public class Demo03 {
   
    public static void main(String[] args) {
   
        //1.new Thread(){ //子类类体 }.start();
        new Thread(){
   
            @Override
            public void run() {
   
                for (int i = 0; i < 30; i++) {
   
                    System.out.println("敌人开枪:" + i);

                }
            }
        }.start();

        //2.new Thread(new Runnable(){//Runnable的子类类体}).start();
        new Thread(new Runnable() {
   
            @Override
            public void run() {
   
                for (int i = 0; i < 30; i++) {
   
                    System.out.println("狙击手开枪:" + i);

                }
            }
        }).start();

        for (int i = 0; i < 30; i++) {
   
            System.out.println("我开枪:" + i);

        }
    }
}

Thread类的常用方法

  1. 当我们使用第一种方式的时候,自定义类,继承了Thread,也会继承一些方法:
    1).run():线程的工作方法。
    2).start():线程启动的方法。
    3).getName():获取线程名称。任何一个线程天生就有名字:Thread-[索引]
    4).setName(String name):设置线程名称。
    5).public static void sleep(long m):让当前线程休息指定的毫秒值。
    6).public static Thread currentThread():获取当前的Thread对象。
代码演示
public static void main(String[] args) {
   
    //使用匿名内部类的方式实现线程
    new Thread(){
   
        @Override
        public void run() {
   
            for (int i = 0; i < 30; i++) {
   
                String format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
                System.out.println(format);
                try {
   
                    Thread.sleep(1000);//让当前线程休息1秒
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }

            }
        }
    }.start();
}

高并发及线程安全

多线程的运行原理

  1. 当多个线程同时运行时,相互独立,互不干扰。
图解代码

在这里插入图片描述

高并发及线程安全的概念

  1. 什么是“高并发”:指多个线程在瞬间同时访问系统的情况。例如:秒杀、12306。
  2. 线程安全:如果多个线程无序执行,这多个线程还同时访问一个资源(一个商品、一个变量、一个文件…),由于多个线程无序、不可控的,这样就可能造成最终的结果不是我们预期的结构,这样就造成了业务数据错误、系统异常。
  3. 在多线程高并发情况下会产生的线程安全性问题:
    1).访问变量的:可见性问题
    2).访问代码的:有序性问题
    3).访问代码的:原子性问题

高并发问题一:可见性

指:多个线程访问同一个变量时,一个线程对变量进行了修改,而第二个线程由于访问这个变量的速度太快,不能及时看到这个变量被其它线程修改了。
这是Java多线程的“运行机制”导致的。

代码演示:
package com.itheima.demo07_多线程运行的原理;

public class MyThread extends Thread {
   
    @Override
    public void run() {
   
        for (int i = 0; i < 30; i++) {
   
            System.out.println(this.getName() + "敌人开枪:" + i);

        }
    }
}

测试类

package com.itheima.demo07_多线程运行的原理;

public class Demo07 {
   
    public static void main(String[] args) {
   
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();

        t1.start();
        t2.start();

    }
}
代码图解:

在这里插入图片描述

Java内存模型JMM

每个线程一个“独立的栈区”,每个线程的代码都在各自的“栈区”内部运行,相互独立,互不干扰。

代码图解

结合可见性问题看内存图
在这里插入图片描述

高并发问题二:有序性

  1. 有序性:指:编译器在不影响最终结果的前提下,在编译时,会对某些代码进行“重排”,为了提高效率。
    例如:
    int a = 10;
    int b = 20;
    int c = a + b;//前两行代码的编译顺序不会影响的最终结果,这时为了提高效率,编译器可能会对上面的两行代码进行重排。

     		int b = 20;
     		int a = 10;
     		int c = a + b;
    
  2. 但是,在“多线程环境下”,如果对代码进行重排,可能会造成不同的结果,这是我们不希望看到的。

代码图解

在这里插入图片描述

高并发问题三:原子性

代码演示
package com.itheima.demo09_高并发问题三_原子性;

public class MyThread extends Thread {
   
    //1.定义一个"公有、静态"变量
    //为了让多个线程访问的是同一个变量
    public static int money = 0;

    //2.run()方法

    @Override
    public void run() {
   
        for (int i = 0; i < 100000; i++) {
   
            money++;
        }
        System.out.println(this.getName() + " 完成");
    }
}

测试类

package com.itheima.demo09_高并发问题三_原子性;

public class Demo09 {
   
    public static void main(String[] args) throws InterruptedException {
   
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        t1.start();
        t2.start(
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值