Java学习笔记8

本文详细讲解了进程与线程的概念,探讨了分时和抢占式调度,区分了同步与异步,并重点介绍了线程安全与不安全的处理方法,包括synchronized、Runnable与Callable的使用,以及线程池(ExecutorService)的各种实现。
摘要由CSDN通过智能技术生成

线程与进程

进程

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

线程

  1. 是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程
  2. 线程实际上是在进程的基础之上的进一步划分,一个进程启动之后,里面的若干个执行路径又可以划分成若干个线程

线程的调度

分时调度

所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间

抢占式调度

优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性)Java使用的是抢占式调度
多线程并不能提高程序的运行速度,但是能提高程序的运行效率,让CPU的使用率更高

同步与异步,并发与并行

同步——>线程安全

排队执行,效率低但是数据安全

异步——>线程不安全

同时执行,效率高但是数据不安全

并发

两个或者多个事件在同一时间段内发生

并行

两个或者多个事件,在同一时刻发生

实现多线程

方法1——Thread

通过继承Thread,重写run方法
run方法就是线程要执行的任务方法
这个执行路径的触发方式,不是调用run方法,而是通过thread对象的start方法来启动任务

每个线程都拥有自己的栈空间,共用一份堆内存

方法2——Runnable

  1. 创建一个任务对象Runnable
  2. 创建一个线程,并为其分配一个任务
  3. 执行这个线程

相对于Thread:
实现Runnable和继承Thread相比,有如下优势

  1. 通过创建任务,然后给线程分配的方式来实现多线程,更适合多个线程同时执行相同任务的情况
  2. 可以避免单继承带来的局限性
  3. 任务与线程本身是分离的,提高了程序的健壮性
  4. 后续学习的线程池技术,接受Runnable类型的任务,而不接收Thread类型的线程

Thread类

getName——返回线程的名称
getId——返回线程的标识符
getPriority——返回线程的优先级,setPriority——设置线程的优先级
getState——返回线程的状态
start——启动一个线程
sleep——休眠线程
setDaemon——标记是守护线程或者用户线程
用户线程可以自定义死亡时间,但是守护线程不可以
当所有用户线程全部死亡以后,守护线程才会死亡

设置和获取线程名称

方法一

通过新建任务的方式,初始化线程的名称

方法二

通过,Thread.currentThread().setName()设置线程的名称
currentThread()——当前正在执行的线程对象

线程中断

一个线程是一个独立的执行路径,他是否应该结束,应该由其自身决定
线程.interrupt——给线程添加终端标记

守护线程

用户线程:当一个进程不包含任何存活的用户线程时,进程结束
守护线程:当最后一个用户线程结束时,所有守护线程自动死亡

线程安全问题

处理线程不安全的方法

1. synchronized同步代码块(隐式锁)

!!! 看同一把锁,才能保证排队执行,才能实现数据安全

//语法
synchronized(锁对象){

}
public class Demo7 {
    public static void main(String[] args) throws InterruptedException {
        Runnable r = new MyThread();
        new Thread(r).start();
        new Thread(r).start();
        new Thread(r).start();
    }
}

class MyThread implements Runnable {
    private int count = 10;
    private Object o = new Object();//创建一个锁对象,这表示同一个锁

    @Override
    public void run() {
        while (true) {
            synchronized (o) {//上锁
                if (count > 0) {
                    System.out.println("正在准备卖票");
                    count--;
                    System.out.println(Thread.currentThread().getName() + "余票" + count);
                } else {
                    break;
                }
            }
            try {
                Thread.sleep(1000);//结束后,休眠1秒,让其他线程能抢到时间片,防止上一个线程快速抢到时间片
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2. 同步方法(隐式锁)

同步方法的锁有两种

  1. this
  2. 如果是静态方法就是类名.class
public class Demo8 {
    public static void main(String[] args) throws InterruptedException {
        Runnable r = new MyThread2();
        new Thread(r).start();
        new Thread(r).start();
        new Thread(r).start();
    }
}

class MyThread2 implements Runnable {
    private int count = 10;
    private Object o = new Object();

    @Override
    public void run() {
        while (true) {
            boolean sale = sale();
            if (!sale)
                break;
            try {
                Thread.sleep(1);//结束后,休眠1秒,让其他线程能抢到时间片,防止上一个线程快速抢到时间片
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public synchronized boolean sale() {
        if (count > 0) {
            System.out.println("正在准备卖票");
            count--;
            System.out.println(Thread.currentThread().getName() + "余票" + count);
            return true;
        }
        return false;
    }
}

3. Lock(显示锁)

Lock子类ReentrantLock
通过lock方法和unlock方法,加锁和解锁


public class Demo9 {
    public static void main(String[] args) throws InterruptedException {
        Runnable r = new MyThread3();
        new Thread(r).start();
        new Thread(r).start();
        new Thread(r).start();
    }
}

class MyThread3 implements Runnable {
    private int count = 10;
    private Object o = new Object();
    private Lock l = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            l.lock();
            if (count > 0) {
                System.out.println("正在准备卖票");
                count--;
                System.out.println(Thread.currentThread().getName() + "余票" + count);
            }
            l.unlock();
            try {
                Thread.sleep(1);//结束后,休眠1秒,让其他线程能抢到时间片,防止上一个线程快速抢到时间片
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count <= 0)
                break;
        }
    }
}

公平锁,非公平锁

公平锁:先来先得到
非公平锁:抢时间片——Java基本上都是

线程死锁

在任何有可能产生锁的方法里,不要再调用另外一个方法,产生另外一个锁

线程的六种状态

  1. BLOCKED——线程的线程状态被阻塞等待监视器锁定
  2. NEW——尚未启动的线程的线程状态
  3. RUNNABLE——可运行的线程的线程状态
  4. TERMINATED——终止线程的线程状态
  5. TIMED_WAITING——具有指定等待时间的等待线程的线程状态
  6. WAITING——等待线程的线程状态

带返回值的线程Callable——第三种实现多线程的方式

Callable的使用步骤

  1. 编写类实现Callable接口,实现call方法
calss XXX implements Callsble<T>{
	@Override
	public <T> call() throws Exception{
		return T;
	}
}
  1. 创建FutureTask对象,并传入第一步编写的Callable对象
	FutureTask<Integer> future = new FutureTask<>(callable);
  1. 通过Thread.start()来启动线程
	new Thread(future).start();

Runnable和Callable的相同点

  • 都是接口
  • 都可以编写多线程程序
  • 都采用Thread.start()启动线程

Runnable和Callable的不同点

  • Runnable没有返回值,Callable可以返回执行结果
  • Callable接口的call()允许抛出异常,Runnable的run()不能抛出

线程池——ExecutorService

ExecutorService service = Executors.newCachedThreadPool();
//通过这个代码创建线程池

频繁的创建线程和关闭线程会浪费大量资源时间
使用线程池的好处

  • 降低资源的消耗
  • 提高响应速度
  • 提高线程的可管理性

缓存线程池(长度无限制)

任务加入后的执行流程

  1. 判断线程池是否存在空闲线程
  2. 存在则使用
  3. 不存在,则创建线程,并放入线程池,然后使用
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * 缓存线程池
 */
public class Demo10 {
    public static void main(String[] args) {
        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() + "线程运行");
            }
        });
        try {
            Thread.sleep(100);//休眠100ms,确定休眠后执行的任务使用的是线程池中的缓存的线程
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "线程运行");
            }
        });
    }
}

定长线程池(长度是执行的数值)

任务加入后的执行流程

  1. 判断线程池是否存在空闲线程
  2. 存在则使用
  3. 不存在空闲线程,且线程池未满的情况下,创建线程,并放入线程池然后使用
  4. 不存在空闲线程,且线程池已满的情况下,等待线程池存在空闲线程
	ExecutorService service = Executors.newCachedThreadPool(线程池长度);

单线程线程池(长度只有1)

执行流程

  1. 判断线程池 的那个线程,是否空闲
  2. 空闲则使用
  3. 不空闲,则等待,池中的单个线程空闲之后,使用
	ExecutorService service = Executors.newCachedThreadPool(线程池长度);
	或
	ExecutorService service2 = Executors.newSingleThreadExecutor();

周期定长线程池

执行流程

  1. 判断线程池是否存在空闲线程
  2. 存在则使用
  3. 不存在空闲线程,且线程池未满的情况下,则创建线程,并放入线程池,然后使用
  4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程

周期性任务执行时:
定时执行,当某个时机出发时,自动执行某任务

	ScheduledExecutorService service3 = Executors.newScheduledThreadPool(2);
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Demo11 {
    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
        /**
         * 定时执行一次任务
         * 参数1,定时执行的任务
         * 参数2,时长数字
         * 参数3,时长数字的单位TimeUtil工具类的时间常量来指定
         */
        service.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行任务");
            }
        },3, TimeUnit.SECONDS);//3秒后执行
        /**
         * 周期执行任务
         * 参数1.任务
         * 参数2.延迟时长数字(第一次执行在什么时间以后)
         * 参数3.周期时长数字(每隔多久执行一次)
         * 参数4.时长数字的单位
         */
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("执行");
            }
        },2,2,TimeUnit.SECONDS);
    }
}


/**
* 周期执行任务
* 参数1.任务
* 参数2.延迟时长数字(第一次执行在什么时间以后)
* 参数3.周期时长数字(每隔多久执行一次)
* 参数4.时长数字的单位
*/
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(“执行”);
}
},2,2,TimeUnit.SECONDS);
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值