java多线程详解与案例+线程安全(上)

目录

1.概念

1.1.什么是进程

 1.2.什么是线程

1.3.进程和线程的区别

1.4.线程的组成

1.5.线程的特点

2.java如何实现多线程

第一种方式: 继承Thread类并重写run方法

1.获取线程名称

 2.为线程起名

 案例1:

第二种方式:实现Runnable接口

案例2:

3.Thread类中常用的方法

(1)休眠:  

 (2)放弃:      

 (3)加入:    

 (4)优先级:      

(5)守护线程:      

 4.多线程安全问题

在程序应用中,如何保证线程的安全性?

(1)同步方式


1.概念

1.1.什么是进程

  • 进程就是正在运行的程序,是系统进行资源分配基本单位
  • 目前操作系统都是支持多进程,可以同时执行多个进程,通过进程ID区分
  • 单核CPU在同一时刻,只能有一个进程;宏观并行,微观串行

 1.2.什么是线程

线程,又称轻量级进程(Light Weight Process)。 进程中的一条执行路径,也是CPU的基本调度单位。 一个进程由一个或多个线程组成,彼此间完成不同的工作, 同时执行,称为多线程

迅雷是一个进程,当中的多个下载任务即为线程。
Java虚拟机是一个进程,当中默认包含主线程(main),可通过代码创建多个独立线程,与main并发执行。

1.3.进程和线程的区别

  • 进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位
  •  一个程序运行后至少有一个线程
  •  一个进程可以包含多个线程,但是至少需要有一个线程,否则这个进程是没有意义
  •  进程间不能共享数据段地址,但是同进程的线程之间可以

1.4.线程的组成

  • CPU时间片: 操作系统(OS)会为每个线程分配执行时间  
  • 运行数据
  •          堆空间: 存储线程需要的对象,多个线程可以共享堆中的数据                
  •          栈空间:  存储线程需使用的局部变量,每个线程都拥有独立的栈
  • 线程的逻辑代码

1.5.线程的特点

线程抢占式执行 :效率高 可防止单一线程长时间独占CPU

② 在单核CPU中,宏观上同时执行,微观上按顺序执行

2.java如何实现多线程

第一种方式: 继承Thread类并重写run方法

①继承Thread  ②重写run方法  ③创建线程对象  ④开启线程

代码:

//1.继承Thread类
public class MyThread extends Thread{

    //2.重写run方法:线程的执行任务
      @Override
      public void run(){
            for (int i = 0 ; i < 20 ; i++){
                System.out.println("i======"+i);
            }
        }
//==================================================================
//TODO 下方测试代码再创建一个类编写,此处是为方便整理
    public static void main(String[] args) {

        //3.创建线程对象
        MyThread myThread = new MyThread();
        //4.开启线程,---当获取cpu时间片,那么该线程就会执行run方法的任务代码
        myThread.start();

        for (int j = 0 ; j < 20 ; j++){
            System.out.println("主方法main====="+j);
        }
    }
}

1.获取线程名称

第一种: 通过父类Thread中的getName()可以获取线程名称。必须为Thread的子类

第二种: 通过Thread类中的静态方法currentThread获取当前线程,getName()获取线程名。任意处获取线程名。

 2.为线程起名

第一种:通过setName()为线程起名

第二种:通过构造函数

 案例1:

需求:有四个窗口,它们卖了100张票

第二种方式:实现Runnable接口

  实现Runnable / 重写run方法 / 创建线程对象 /开启线程

 记住: 能实现接口的就不要继承父类。因为父类只需要单继承。扩展性比较差

案例2:

需求:有四个窗口,它们卖了100张票

  •  以上会出现多个窗口公卖同一张票,而且也会出现超卖的现象。
  • 多线程操作公共数据时出现的线程安全问题。后面再解决。

3.Thread类中常用的方法

(1)休眠:  

   public static void sleep(long millis)      当前线程主动休眠millis毫秒

 (2)放弃:      

           public static void yield()     当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片

 (3)加入:    

        public final void join()       允许其他线程加入到当前线程中,直到其他线程执行完毕后,当前线程才会执行

 (4)优先级:      

 线程对象.setPriority()      线程优先级1-10,默认为5,优先级越高,表示获取CPU的概率越高。

 优先级有概率,就算设置了也无法决定硬件的CPU

(5)守护线程:      

  •           线程对象.setDaemon(true);设置为守护线程。      
  •           线程有两类:用户线程(前台线程)和守护线程(后台线程)      
  •           如果程序中所有前台线程都执行完毕了,后台线程也会自动结束。      
  •           垃圾回收线程属于守护线程。

 4.多线程安全问题

  • 当多线程并发访问临界资源时,如果破坏原子操作,可难会造成数据不一致
  • 临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性
  • 原子操作:不可分割的多步操作,被视作一个整体,其顺序和步骤不可打乱或缺省

多线程安全问题演示:

 需求: A线程将“Hello”存入数组;B数据将“World”存入数组。


public class Test05 {
//创建数组
private static String arr[]=new String[2];
//下标
private static int index=0;

public static void main(String[] args) throws InterruptedException {
//需求: A线程将“Hello”存入数组;B数据将“World”存入数组。
    Thread t1 = new Thread(new Runnable() { //匿名内部类
        @Override
        public void run() {
            if (arr[index]==null){
                arr[index]="hello";
                index++;
            }
        }
    });
    Thread t2 = new Thread(new Runnable() { //匿名内部类
        @Override
        public void run() {
            if (arr[index]==null){
                arr[index]="world";
                index++;
            }
        }
    });
    //开启线程
    t1.start();
    t2.start();

    t1.join();
    t2.join();
    //t1和t2执行完毕才执行下方的打印
    System.out.println(Arrays.asList(arr));
}
}

在程序应用中,如何保证线程的安全性?

(1)同步方式

同步代码块:synchronized(临界资源对象){   //对临界资源对象加锁

                       //代码(原子操作)

                      }

注意:

  •          每个对象都有一个互斥锁标记,用来分配给线程的
  •          只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块
  •          线程退出同步代码块,会释放相应的互斥锁标记

修改上方演示代码 :加入synchronized


public class Test05 {
//创建数组
private static String arr[]=new String[2];
//下标
private static int index=0;

public static void main(String[] args) throws InterruptedException {
//需求: A线程将“Hello”存入数组;B数据将“World”存入数组。
    Thread t1 = new Thread(new Runnable() { //匿名内部类
        @Override
        public void run() {
            //synchronized 同步(互斥锁)
            synchronized (arr){ //同步代码块 必须获取()锁资源才能进入同步代码块中
                 //这里的操作必须原子操作要求
                if (arr[index]==null){
                    arr[index]="hello";
                    index++;
                }
            }
        }
    });
    Thread t2 = new Thread(new Runnable() { //匿名内部类
        @Override
        public void run() {
            synchronized (arr){
                if (arr[index]==null){
                    arr[index]="world";
                    index++;
                }
            }
        }
    });
    //开启线程
    t1.start();
    t2.start();

    t1.join();
    t2.join();
    //t1和t2执行完毕才执行下方的打印
    System.out.println(Arrays.asList(arr));

}
}

   解决案例2的线程安全问题    (this)临界资源对象

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值