JAVA基础学习笔记_多线程

文章目录

多线程

线程是操作系统能够运算调度的最小单位,被包含在进程之中

就是让程序做多件事情

并发和并行

并发,在同一时刻,有多个指令在单个CPU上交替 执行

并行,在同一时刻,多个指令在多个CPU上同时执行

多线程的实现方式

Thread类实现

public class MyThread extends Thread{
    @Override
    public void run(){
        for(int i= 0;i<100;i++){
            System.out.println(getName()+"hello");
        }
    }
}

//自己定义一个类继承thread
//重写run方法
//创建子类的对象,启动线程
public static void main(String[] args) {
	MyThread t1 = new MyThread();
	MyThread t2 = new MyThread();
	t1.setName("线程1");
	t2.setName("线程2");
	t1.start();
	t2.start();
}

Runnable接口方式实现

接口的意义,java是单继承方式的,所以为了扩展性和灵活性有个接口

public class MyRun implements Runnable{
    @Override
    public void run() {
        for(int i= 0;i<100;i++){
            //调用当前线程的静态方法,获取当前线程对象
            Thread t = Thread.currentThread();
            System.out.println(t.getName()+"hello");
            //或者用链式编程写成以下
            System.out.println(Thread.currentThread().getName()+"hello");
        }
    }
}

public static void main(String[] args) {
	MyRun mr = new MyRun();
	Thread t1 = new Thread(mr);
	Thread t2 = new Thread(mr);
	t1.setName("线程1");
	t2.setName("线程2");
	t1.start();
	t2.start();
}

callable接口和Future接口实现

public class Mycallable implements Callable<Integer> {
    //前面两种方式不能返回线程的结果,但这个可以
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 100; i++) {
            sum = sum+i;
        }
        return sum;
    }
}

public static void main(String[] args) throws ExecutionException, InterruptedException {
	Mycallable mc = new Mycallable();
	//用FutureTask对象管理运行结果
	FutureTask<Integer> ft = new FutureTask<>(mc);
	Thread t1= new Thread();
	t1.start();
	Integer result = ft.get();
	System.out.println(result);
}

常用的成员方法

getName()//返回线程的名称,在main方法中不指定是那个线程,这个线程的名字就是main线程,跟linux系统编程一样,同属一个祖宗
setName()//设置线程的名字
static Thread currentThread()//获取当前线程的对象
static void sleep();设置休眠时间
//优先级方法,抢占式调度,优先级越高,抢到cpu概率是越大的,只是概率大
setPriority()//设置线程的优先级
getPriority()//获得线程优先级
//守护线程,当其他的非守护线程执行完毕,守护线程会陆续结束,没有存在的必要了
//比如聊天窗口线程结束,那么传输文件的线程也就没必要存在了
setDaemon()//设置为守护线程
//礼让线程
yield()//出让当前CPU的执行权
//插入线程
t.join()//把t线程插入到当前线程之前

线程的生命周期

  • 新建,创建线程
  • 就绪,有执行资格没有执行权
  • 运行,有执行资格有执行权
  • 阻塞,没有执行资格没有执行权
  • 死亡,线程死亡变成垃圾

线程的安全问题

线程执行时,有随机性

同步代码块

锁就是大门钥匙,谁拿到了,谁就可以进到对应的程序里面

//三个窗口买票,共卖一百张票
public class MyThread extends Thread{
    static int ticket = 0;
    static Object obj = new Object();
    public void run(){
        while(true){
            //同步代码块
			//锁,就类似于sql的事务
            synchronized (obj){
                if(ticket<100){
                    try{
                        Thread.sleep(10);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    ticket++;
                    System.out.println(getName()+"正在卖第"+ticket+"张票");
                }
            }
        }
    }
}

同步方法

private synchronized boolean extracted()

  • 同步方法是锁方法里面所有的代码

  • 锁对象不能自己指定,如果是非静态方法,this,如果是静态方法是当前的字节码文件

    public class MyRun implements Runnable{
    int ticket = 0;
    @Override
    public void run() {
    while(true){
    if (extracted()) break;
    }
    }
    private synchronized boolean extracted() {
    if (ticket == 100) {
    return true;
    }
    else{
    try {
    Thread.sleep(10);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    ticket++;
    System.out.println(Thread.currentThread().getName() + "正在卖第" + ticket + "张票");
    }
    return false;
    }
    }

lock锁

手动加锁和释放

public class MyThread extends Thread{
    static int ticket = 0;
    static Lock lock = new ReentrantLock();//Lock是接口,要新建实现类
    @Override
    public void run() {
        while (true){
            lock.lock();;
            try{
                if(ticket ==100){
                    break;
                }else{
                    Thread.sleep(10);
                    ticket++;
                    System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
                }
            }
            catch (Exception e){
                e.printStackTrace();
            }
            finally {//finally,无论有无异常,这里的代码都会执行
                lock.unlock();
            }
        }
    }
}

死锁

线程A占着A锁,线程B拿着B锁,但是两个线程此时想访问对方的锁,就死锁了

生产者和消费者(等待唤醒机制)

线程的执行具有随机性,所以等待唤醒机制可以让执行均匀

比如,桌子上有食物,消费者(线程A)开吃,桌子没有食物,厨师(线程B)开做

执行代码

public static void main(String[] args) throws ExecutionException, InterruptedException {
	Cook c= new Cook();
	Foodie f = new Foodie();
	c.setName("厨师");
	f.setName("吃货");
	c.start();
	f.start();
}

厨师逻辑

public class Cook extends Thread {
    public void run() {
        while (true) {
            synchronized (Desk.lock) {
                if (Desk.count == 0) {
                    break;
                } else {
                    if (Desk.foodFlag == 1) {
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } else {
                        System.out.println("厨师正在做第" + Desk.count + "个汉堡");
                        Desk.foodFlag = 1;
                        Desk.lock.notifyAll();
                    }
                }
            }
        }
    }
}

吃货逻辑

public class Foodie extends Thread{
    public void run(){
        while(true){
            synchronized (Desk.lock){
                if(Desk.count==0){
                    break;
                }else{
                    if (Desk.foodFlag==0) {
                        try {
                            Desk.lock.wait();//释放当前锁对象,直到有人唤醒
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } else {
                        Desk.count--;
                        System.out.println("吃货正在吃第"+Desk.count+"个汉堡");
                        Desk.lock.notifyAll();//唤醒所有线程
                        Desk.foodFlag=0;
                    }
                }
            }
        }
    }
}

桌子逻辑

/**
 * 控制生产者和消费者的执行
 */
public class Desk {
    //0,没有面条,1,有面条
    public static int foodFlag = 0;
    //总个数
    public static int count = 10;
    //锁对象
    public static  Object lock = new Object();
}

阻塞队列实现等待唤醒机制

队列机制,两个线程在一个队列中取和存数据

线程的6种状态

并没有运行状态,为了好理解,可以虚拟出这么一个状态

  • 新建
  • 就绪
  • 阻塞
  • 等待wait(),无执行资格无执行权
  • 计时sleep(),无执行资格无执行权
  • 结束

线程池

  • 提交任务时,池子会创建新的线程,执行完毕后,线程归还给池子,再提交任务是,不需要创建新的线程,用已有的线程

  • 提交任务没有空闲线程,也无法创建新线程,任务排队等待

    public static void main(String[] args) throws ExecutionException, InterruptedException {
    //Executors.newCachedThreadPool()//无限线程池
    //Executors.newFixedThreadPool()//有上限的线程池
    //创建线程池对象
    ExecutorService pool1 = Executors.newFixedThreadPool(3);
    //提交任务
    pool1.submit(new MyRun());
    pool1.submit(new MyRun());
    pool1.submit(new MyRun());
    pool1.submit(new MyRun());
    pool1.submit(new MyRun());
    }

自定义线程池

  • 当核心线程满时,再提交会排队

  • 当核心线程满,队伍满时,会创建临时线程

  • 当核心线程满,队伍满,临时线程满时,会触发任务拒绝策略

    public static void main(String[] args) {
    new ThreadPoolExecutor(
    3,//核心线程数
    6,//最大线程数
    60,//存活时间
    TimeUnit.SECONDS,//存活时间时间单位
    new ArrayBlockingQueue<>(3),//阻塞队列长度
    Executors.defaultThreadFactory(),//线程工厂
    new ThreadPoolExecutor.DiscardPolicy()//拒绝策略
    );
    }

最大并行数

我的电脑是32核32线程最大并行,超线程技术可以64线程,是因为实现让1个核分身同时为两个线程服务

线程池合适的大小

CPU密集型运算,最大并行数+1

I/O密集型运算,最大并行数期望CPU利用率((CPU计算时间+等待时间)/CPU计算时间)

多线程的额外扩展

  • volatile
  • jMM
  • 悲观锁\乐观锁\CAS
  • 原子性
  • 并发工具类
相关推荐
炸毛的飞鼠几秒前
虚拟机Ubuntu以及pwn的工具安装
笔记·学习·ubuntu
2401_857610033 分钟前
剖析 SSM 校园一卡通密钥管理系统 PF 技术架构中安全机制的深度融合
java·数据库·安全
qwe35263311 分钟前
机器视觉,opencv基础学习(一)
学习
HUT_Tyne26513 分钟前
力扣--LCR 131. 砍竹子1
java·算法·leetcode
啊松同学21 分钟前
【Spring】使用@Async注解后导致的循环依赖问题
java·后端·spring
daiyang123...27 分钟前
JavaEE 【知识改变命运】05 多线程(4)
java·单例模式·java-ee
zzxxlty35 分钟前
Intellij IDEA 2023 获取全限定类名
java·ide·intellij-idea
悟空非空也38 分钟前
178K⭐排名第一计算机面试笔记
笔记·面试·职场和发展
coding侠客41 分钟前
避免版本冲突:Spring Boot项目中正确使用Maven的DependencyManagement
java·spring boot·maven
sky丶Mamba1 小时前
Java虚拟机启动时默认携带参数(jdk8)
java·jvm