(26)程序,线程,进程的理解

01,    什么是程序?
02,    什么是进程?
03,    什么是线程?
04,    单核CPU和多核CPU的理解
05,    并行与并发
06,    使用多线程优点
07,    创建多线程的方式
08,    测试Thread中的常用方法
09,    线程优先级
10,    比较创建线程的两种方式
11,    线程的分类
12,    线程的生命周期
13,    线程安全问题

程序,线程,进程的概念
程序(program):是为了完成特定任务,用某种语言编写的一组指令的集合。即指一段静态代码,静态对象。

进程(process):正在运行的一个程序,是一个动态的过程:它有自身的产生,存在和消亡的过程。-----生命周期
  • 程序是静态的,进程是动态的
  • 进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
线程(thread):进程可进一步细化为线程,是一个程序内部的一条执行路径
  • 若一个进程同一时间并执行多个线程,就是支持多线程
  • 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计算器(pc),线程切换的开销小
  • 一个进程终端多个线程共享相同的内存单元/内存地址空间–>它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便,高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。
单核CPU和多核CPU的理解
  • 单核CPU,其实是一种假的多线程,因为在一个时间单元内,只能执行一个线程的任务。
    例如:虽然有多车道,但是收费站只有一个工作人员在收费,只有收了费才能通过,那么CPU就好比收费人员。但是因为CPU时间单元特别短,因此感觉不出来

  • 如果是多核的话,才能更好的发挥多线程的效率。(现在的服务器都是多核的)

  • 一个Java应用程序java.exe,其实至少有三个线程:main主线程,gc()垃圾回收线程,异常处理线程。当然如果发送异常,会影响主线程

并行与并发
  • 并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
  • 并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀,多个人做同一件事。
使用多线程优点

1,提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
2,提高计算机系统CPU的利用率
3,改善晨曦结构,将既长又复杂的进程分为多个线程,独立运行,利于理解和修改

创建多线程的方式

1,继承Thread,重写run方法,不可以通过run方式启动线程,要通过对象调start():启动当前线程,调用当前线程的run()。

2,实现Runnable接口,重写run方法,将此对象作为参数传递到Thread类的构造器中,创建Thread对象,调用start

3,实现Callable接口

public static void main(String[] args) {
        //3,创建Callable接口实现类的对象
        NumThread t1 = new NumThread();
        //4,将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
        FutureTask<Integer> futureTask = new FutureTask<>(t1);
        //5,将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,调用start方法
        new Thread(futureTask).start();
        try {
            //6,获取Callable中call方法的返回值
            //get()返回值即FutureTask构造器参数Callable实现类重写call()的返回值。
            Integer sum = futureTask.get();
            System.out.println("总和为"+sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

//1,创建一个实现Callable的实现类
class NumThread implements Callable<Integer>{

    //2,实现call方法,将此线程需要执行的操作声明在call中
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }

Callable接口为什么比Runnable接口创建多线程方式更强大的原因

  • 1,call()可以有返回值的
  • 2,call()可以抛出异常,被外面的操作捕获,获取异常信息
  • 3,Callable是支持泛型的

4,线程池的使用

 public static void main(String[] args) {
        //1,提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1 = (ThreadPoolExecutor)service;
        service1.setCorePoolSize(16);
//        System.out.println(service.getClass());//设置线程池的属性
        //2,执行指定的线程操作。需要提供实现Runnable接口或Callable接口实现类的对象
        service.execute(new NewThread());//适用于Runnable
        service.execute(new NewThread1());//适用于Runnable

//        service.submit(Callable callable);//适用于Callable
        //关闭连接池
        service.shutdown();
    }
}

class NewThread implements Runnable{

    @Override
    public void run() {
        for (int i=1;i<=100;i++){
            if(i%2 == 0){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}
class NewThread1 implements Runnable{

    @Override
    public void run() {
        for (int i=1;i<=100;i++){
            if(i%2 != 0){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
  • 使用线程池的好处:
  • 1,提高响应速度(减少了创建新线程的时间)
  • 2,降低资源消耗(重复利用线程池中线程,不需要每次都创建)
  • 3,便于线程管理
    corePoolSize:核心池的大小
    maximumPolSize:最大线程数
    keepAliveTime:线程没有任务时最多保持多长时间后会终止
测试Thread中的常用方法

1,start():启动当前前程,调用当前线程的run()
2,run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
3,currentThread():静态方法,返回执行当前代码的线程
4,getName():获取当前线程的名字
5,setName():设置当前线程的名字
6,yield():释放当前cpu的执行权
7,join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后结束阻塞状态。
8,stop():已过时。当执行此方法时,强制结束当前线程。
9,sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态
10,isAlive():判断当前线程是否存活

线程优先级

1,MAX_PRIORITY 最高优先级 10
2,MIN_PRIORITY 最低优先级 1
3,NORM_PRIORITY 默认优先级 5

  • 如何获取和设置当前线程的优先级:

    1,getPriority():获取线程的优先级
    2,setPriority():设置线程的优先级
    (说明:从概率上说,高优先级的线程高概率的情况下被执行,并不意味着只有当高优先级的线程执行完之后,低优先级的线程才执行)

  • 线程的通信

    wait() / notify() notifyAll():此三个方法定义在Object类中

比较创建线程的两种方式

开发中:优先选择:实现Runnable接口的方式

原因:
1,实现的方式没有类的单继承性的局限性
2,实现的方式更适合来处理多个线程有共享数据的情况

线程的分类

1,守护线程

2,用户线程

总结:它们几乎每个方面都是相同的,唯一的区别就是判断JVM何时离开,守护线程是用来服务用户线程的,通过在start方法前调用thread.setDaemon(true)可以把一个用户线程变成一个守护线程,Java垃圾回收就是一个典型的守护线程

线程的生命周期

在这里插入图片描述

线程安全问题

加锁

例题:
问题:卖票的过程中,出现了重票,错票
出现原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来也操作车票

解决办法
当线程a操作时,其他线程不能参与进来,直到a完成操作,及时线程a出现阻塞,也不可以改变

方式一:同步代码块

	synchronized(同步监视器){//类.class
	      //需要被同步的代码(操作共享数据的代码)
}

// 同步监视器:锁 – > 任何一个对象都可以充当锁,要求:多个线程必须要共用同一把锁

方式二:同步方法

	synchronized 返回参数 方法名(){
}

方式三:同步锁 Lock( jdk 1.5 后 ), 注意:是一个显示锁,需要通过 lock() 方法上锁,必须通过 unlock() 方法进行释放锁

Lock lock = ...;
lock.lock();
try{
    //处理任务
}catch(Exception ex){
     
}finally{
    lock.unlock();   //释放锁
}

总结
1,同步方法仍然涉及到同步监视器,只是不需要我们显示的声明。
2,非静态的同步方法,同步监视器是:this
静态的同步方法,同步监视器是:当前类本身

线程的死锁问题
死锁:
- 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
- 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续

解决方法
专门的算法,原则
尽量减少同步资源的定义
尽量避免嵌套同步


下一章,(27)Java中线程安全的实际应用

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值