多线程问题——JAVA

本文详细讲解了Java线程与进程的区别,同步与异步概念,线程调度策略,如何获取线程名,线程安全问题及解决方案,包括synchronized、Lock,以及Java线程池ExecutorService的四种实现及其优势。
摘要由CSDN通过智能技术生成

一、线程与进程

1.什么是线程?
 线程是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少

有一个线程。
线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分
成若干个线程。

2.什么是进程
进程是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间。

二、同步与异步

同步: 排队执行 , 效率低但是安全.
异步: 同时执行 , 效率高但是数据不安全.

三、线程调度

1.分时调度
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
2.抢占式调度

优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),
Java使用的为

3.抢占式调度。
CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻,

只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时
刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使
用率更高

四、获取线城名称

   打印线程的名称

在这里插入图片描述

五、线程的休眠

sleep
单位(毫秒)
在这里插入图片描述

六、线程的安全

6.1 线程安全问题的展示

既然是线程安全问题,那么毫无疑问,所有的隐患都是在多个线程访问的情况下产生的,也就是我们要确保在多条线程访问的时候,我们的程序还能按照我们预期的行为去执行,我们看一下下面的代码。

public class Demo2 {
      static int count = 0;
    public static void main(String[] args) {

        Thread t = new Thread(() -> {
            while (count<50) {
                count++;
                System.out.println(Thread.currentThread().getName()+"  "+count);
            }
        });
        Thread r = new Thread(() -> {
          while (count<50) {
                count++;
              System.out.println(Thread.currentThread().getName()+"  "+count);
            }
        });
        Thread q = new Thread(() -> {
            while (count<50) {
                count++;
                System.out.println(Thread.currentThread().getName()+"  "+count);
            }
        });
        t.start();
        r.start();
        q.start();
    }
}


对于同一个数据count我们开启了三个线程,观察以下结果~

这里我们可以看到同时出现了两次 2
在这里插入图片描述
出现这种情况显然表明这个方法根本就不是线程安全的,出现这种问题的原因有很多。

最常见的一种,就是我们线程 0 在进入方法后,拿到了count的值,刚把这个值读取出来,还没有改变count的值的时候,结果线程 1 也进来的,那么导致线程 0 和线程 1 拿到的count值是一样的。

那么由此我们可以了解到,这确实不是一个线程安全的类,因为他们都需要操作这个共享的变量。其实要对线程安全问题给出一个明确的定义,还是蛮复杂的,我们根据我们这个程序来总结下什么是线程安全。

当多个线程访问某个方法时,不管你通过怎样的调用方式、或者说这些线程如何交替地执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类是线程安全的。

搞清楚了什么是线程安全,接下来我们看看Java中确保线程安全最常用的两种方式。先来看段代码。

6.2 线程安全问题的解决方法-----上锁
6.2.1 同步代码块

关键字 synchronized
格式
synchronized( 锁对象 ) { 同步代码块 }
咱们看下面代码

public class Demo2 {
      static int count = 0;
    public static void main(String[] args) {
        MyRunnable m = new MyRunnable();
        new Thread(m).start();
        new Thread(m).start();
        new Thread(m).start();
    }
    static class MyRunnable implements Runnable{

        Object o = new Object();     //创建一个锁对象
        @Override

        public  void run() {
            while (true) {
                synchronized (o) {   //上锁
                    if (count < 10) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "  " + count);
                        count++;
                    }
                }      if (count>=10)
                         break;

            }

        }
    }
}

运行结果如下,这就是通过同步代码块解决线程安全问题

在这里插入图片描述

6.2.2 同步方法

原理与同步代码块一致
把同步代码块那部分抽出来组成一个方法
咱们举个例子吧

public class Demo2 {
      static int count = 0;
    public static void main(String[] args) {
        MyRunnable m = new MyRunnable();
        new Thread(m).start();
        new Thread(m).start();
        new Thread(m).start();
    }
    static class MyRunnable implements Runnable{

        Object o = new Object();
        @Override

        public  void run() {
            boolean b = true;
            while (b) {
               b  = sale();

            }

        }
        public synchronized boolean sale(){
            if (count < 10) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "  " + count);
                count++;
            }
            if (count>=10) 
                return false;
            
            return  true;
        }
    }
}

在这里插入图片描述

6.2.3 显示锁 Lock

使用方法如下~


import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo2 {
      static int count = 0;
    public static void main(String[] args) {
        MyRunnable m = new MyRunnable();
        new Thread(m).start();
        new Thread(m).start();
        new Thread(m).start();
    }
    static class MyRunnable implements Runnable{
      Lock l = new ReentrantLock();
        @Override

        public  void run() {
            boolean b = true;
            while (b) {

               b  = sale();


            }

        }
        public  boolean sale(){
            l.lock();
            if (count < 10) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "  " + count);
                count++;
            }
            if (count>=10) {
                l.unlock();
                return false;
            }
            l.unlock();
            return  true;

        }
    }
}

在这里插入图片描述

6.2.4 显示锁与隐式锁的区别

可以参考该文章

点击此处进入

七、线程池 Executors

7.1 线程池的好处
降低资源消耗。
提高响应速度。
提高线程的可管理性。
7.2 Java中的四种线程池 ExecutorService
7.2.1 缓存线程池
 /**
  * 缓存线程池.
  * (长度无限制)
  * 执行流程:
  *   1. 判断线程池是否存在空闲线程
  *   2. 存在则使用
  *   3. 不存在,则创建线程 并放入线程池, 然后使用
  */
 ExecutorService service = Executors.newCachedThreadPool();
 //向线程池中 加入 新的任务
 service.execute(new Runnable() {
   @Override
   public void run() {
     System.out.println("线程的名称:"+Thread.currentThread().getName());
   }
 });
 service.execute(new Runnable() {
   @Override
   public void run() {
     System.out.println("线程的名称:"+Thread.currentThread().getName());
   }
 });
 service.execute(new Runnable() {
   @Override
   public void run() {
     System.out.println("线程的名称:"+Thread.currentThread().getName());
   }
 });
7.2.2 定长线程池
 /**
  * 定长线程池.
  * (长度是指定的数值)
  * 执行流程:
3. 单线程线程池
4. 周期性任务定长线程池
  *   1. 判断线程池是否存在空闲线程
  *   2. 存在则使用
  *   3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
  *   4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
  */
 ExecutorService service = Executors.newFixedThreadPool(2);
 service.execute(new Runnable() {
   @Override
   public void run() {
     System.out.println("线程的名称:"+Thread.currentThread().getName());
   }
 });
 service.execute(new Runnable() {
   @Override
   public void run() {
     System.out.println("线程的名称:"+Thread.currentThread().getName());
   }
 });
7.2.3 单线程线程池
效果与定长线程池 创建时传入数值1 效果一致.
 /**
  * 单线程线程池.
  * 执行流程:
  *   1. 判断线程池 的那个线程 是否空闲
  *   2. 空闲则使用
  *   4. 不空闲,则等待 池中的单个线程空闲后 使用
  */
 ExecutorService service = Executors.newSingleThreadExecutor();
 service.execute(new Runnable() {
   @Override
   public void run() {
     System.out.println("线程的名称:"+Thread.currentThread().getName());
   }
 });
 service.execute(new Runnable() {
   @Override
   public void run() {
     System.out.println("线程的名称:"+Thread.currentThread().getName());
   }
 });
7.2.4 周期性任务定长线程池
public static void main(String[] args) {
 /**
  * 周期任务 定长线程池.
  * 执行流程:
  *   1. 判断线程池是否存在空闲线程
  *   2. 存在则使用
  *   3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
  *   4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
  *
  * 周期性任务执行时:
  *   定时执行, 当某个时机触发时, 自动执行某任务 .
   */
 ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
 /**
  * 定时执行
  * 参数1.  runnable类型的任务
  * 参数2.  时长数字
  * 参数3.  时长数字的单位
  */
 /*service.schedule(new Runnable() {
   @Override
   public void run() {
     System.out.println("俩人相视一笑~ 嘿嘿嘿");
   }
 },5,TimeUnit.SECONDS);
 */
 /**
  * 周期执行
  * 参数1.  runnable类型的任务
  * 参数2.  时长数字(延迟执行的时长)
  * 参数3.  周期时长(每次执行的间隔时间)
  * 参数4.  时长数字的单位
  */
 service.scheduleAtFixedRate(new Runnable() {
   @Override
   public void run() {
     System.out.println("俩人相视一笑~ 嘿嘿嘿");
   }
 },5,2,TimeUnit.SECONDS);
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值