线程

线程安全:多线程操作的共享变量能够返回预期的结果

进程-Process

  • 程序的一次动态执行过程,占用特定地址空间
  • 可包含多个线程
  • 由cpu/data/code组成
  • 资源分配单位

线程-Thread

  • 进程内部的一个执行单元,程序中的一个单元顺序控制流程
  • 调度和执行的单位
  • 只有一个主线程,为系统入口,执行整个程序
  • 线程的运行由调度器调度,调度器与操作系统有关,先后顺序不能人为控制
  • 每个线程都有优先权,优先级高的线程优先于较低的线程执行(1-10)

线程的实现:

  • 继承 Thread 类
  • 实现 Runnable 接口
  • 实现 Callable 接口 - 并发
/**
*	Thread
*/
public class StartThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("搬砖第" + i + "天");
        }
    }

    public static void main(String[] args) {
        //创建
        StartThread st = new StartThread();
        //启动(系统执行)
        st.start();
    }
}

/**
*	Runnable 
*/
public class StartRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println("搬砖第" + i + "天");
        }
    }

    public static void main(String[] args) {
        Thread thread = new Thread(new StartRunnable());
        thread.start();
    }
}

/**
*	Callable 
*/
public class StartCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        String tired = "搬砖累!!!";
        for (int i = 0; i < 50; i++) {
            System.out.println("搬砖第" + i + "天");
        }
        return tired;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建目标对象
        StartCallable sc = new StartCallable();
        //创建执行服务
        ExecutorService service = Executors.newFixedThreadPool(1);
        //提交执行
        Future<String> result = service.submit(sc);
        //获取结果
        String r = result.get();
        //关闭服务
        service.shutdownNow();

    }
}

线程状态:

在这里插入图片描述
在这里插入图片描述

  • 新生状态(New)
    new 创建线程后,线程处于新生状态,且已经分配了自己的内存空间,通过 start 方法进入就绪状态

  • 就绪状态(Runnable)
    处于就绪状态后,线程已经可以运行,但还没被分配到CPU,处于“线程就绪队列”,等待系统为其分配CPU。当获得CPU后,线程就进入运行状态并自动调用自己的 run 方法。
    导致线程进入就绪状态:

    • 新建线程:new 线程,调用 start()
    • 阻塞线程:阻塞解除后,进入就绪状态
    • 运行线程:调用 yield() 方法,直接进入就绪状态
    • 运行线程:JVM将CPU资源从本线程切换到其他线程
  • 运行状态(Running)
    线程执行 run 方法时,直到调用其他方法而终止或等待资源而阻塞或完成任务死亡

  • 阻塞状态(Blocked)
    阻塞指暂停一个线程的执行以等待某个条件发生
    阻塞原因:

    • 执行 sleep() 方法,使线程休眠,进入阻塞,时间到后,线程进入就绪状态
    • 执行 wait() 方法,使当前线程进入阻塞状态;当使用 nofity() 方法唤醒线程后,进入就绪状态
    • 线程运行时,受某个操作进入阻塞,如执行IO流操作,阻塞去除后,进入就绪状态
    • join() 线程联合,当某个线程等待另一个线程执行结算后,才能继续执行时,使用 join() 方法
  • 死亡状态(Terminated)
    线程正常执行完后死亡
    执行 stop() / destory() 方法终止线程后线程死亡

线程方法:

  • sleep():让线程进入阻塞状态,休眠时间到后再进入就绪状态
  • yield():让线程直接进入就绪状态,让出CPU使用权
  • join():联合,使线程1和线程2联合,在线程1中执行线程2的jion()方法,线程1必须等待线程2执行完毕才能继续执行

线程优先级(Priority)

优先级不代表绝对的执行顺序,而是概率,优先级高的可能会优先调用
优先级范围从1(MIN_RPIORITY) - 10(MAX_PRIORITY),默认为5(NORM_PRIORITY)
getPriority(),获取当前线程优先级

用户线程 / 守护线程

线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程的执行完毕
守护线程一般后台记录操作日志/监控内存等,是为用户线程服务

线程同步

同步:协同步调,按预定的先后次序执行
并发:同一个对象被多个线程同时操作
synchronized:

  • 同步方法
    同步方法锁的是this,即对象本身或class
  • 同步块
    synchronized(obj) {},obj称为同步监视器,可以任何对象,优先使用共享资源作为同步监视器

死锁

多个线程各自占有一些共享资源,并且相互等待其他线程占有的资源才能进行,而导致两个或多个线程都在等待对方释放资源,都停止执行的。

死锁由同步块同时持有多个对象锁造成,避免死锁,即不要同时持有两个对象的锁

生产者/消费者模式

生产者:负责生成数据的模块(方法/对象/线程/进程)
消费者:负责处理数据的模块(方法/对象/线程/进程)
缓冲区:消费者不能直接使用生产者的数据,它们之间有个缓冲区,生产者将生产好的数据放入缓冲区中,消费者从缓冲区中拿数据
缓冲区是实现并发的核心,缓冲区的优势:

  • 实现线程的并发协作
    有缓冲区后,生产者只需要往缓冲区中放置数据,消费者只需从缓冲区中获取数据,实现生产者和消费者分离
  • 解耦了生产者和消费者
  • 解决忙闲不均,提高效率
    生产者生产数据慢时,缓冲区仍有数据,不影响消费者消费,消费者处理数据慢时,生产者仍可继续往缓冲区中放置数据
线程并发协作:

线程并发协作-线程通信,通常用于生产者/消费者模式:
1.生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件
2.对于生产者,没有生产产品前,消费者需要进入等待状态,而生产了产品后,又需要马上通知消费者消费
3.对应消费者,在消费之后,需要通知生产者已经消费结算,需要继续生产以供消费
4.在生产者消费者问题中,仅仅synchronized是不够的

  • synchronized可阻止并发更新同一个共享资源,实现同步
  • synchronized不能用来实现不同线程之间的消息通信

5.线程消息通信方法:

  • wait():线程持续等待
  • wait(long time):线程等待指定时间
  • notify():唤醒一个处于等待状态的线程
  • notifyAll():唤醒同一个对象上所以调用wait()方法的线程,优先级别高的线程优先运行

public class ProductorOrConsumer {

    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        new Productor(container).start();
        new Consumer(container).start();
    }
}

// 生产者
class Productor extends Thread {

    SynContainer synContainer;

    public Productor(SynContainer container) {
        this.synContainer = container;
    }

    @Override
    public void run() {
        // 生产
        for (int i = 0; i < 100; i++) {
            System.out.println("生产--》 " + i + "号KFC");
            synContainer.push(new KFC(i));
        }
    }
}

// 消费者
class Consumer extends Thread {

    SynContainer synContainer;

    public Consumer(SynContainer container) {
        this.synContainer = container;
    }

    @Override
    public void run() {
        // 消费
        for (int i = 0; i < 100; i++) {
            KFC pop = synContainer.pop();
            System.out.println("消费--》 " + pop.getFlapper() + "号KFC");
        }
    }
}

// 缓冲区
class SynContainer {
    KFC[] kfcs = new KFC[10];

    int count = 0;

    // 生产
    public synchronized void push(KFC kfc) {
        // 数据满后等待
        if(count == kfcs.length) {
            try {
                // 线程阻塞 需要消费者通知生产解除
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        kfcs[count] = kfc;
        count++;
        // 生产后通知消费解除
        this.notifyAll();
    }

    // 消费
    public synchronized KFC pop() {
        // 没有数据后等待
        if (count == 0) {
            try {
                // 线程阻塞 需要生产者通知消费解除
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        count--;
        KFC k = kfcs[count];
        // 消费后通知生产解除
        this.notifyAll();
        return k;
    }
}

// KFC
class KFC {
    private int flapper;

    public KFC(int flapper) {
        this.flapper = flapper;
    }

    public int getFlapper() {
        return flapper;
    }

    public void setFlapper(int flapper) {
        this.flapper = flapper;
    }
}

Other

任务定时调度

通过Timer和Timetask,实现定时启动某线程
Timer本身就是线程,起调用其他线程的作用

public class TimerTest {
    public static void main(String[] args) {
        // 定义定时器
        Timer t = new Timer();
        // 定义任务
        Task task = new Task();
        // 3秒后执行
        t.schedule(task, 3000);
        // 3秒后执行,每个3秒执行一次
        t.schedule(task, 3000, 3000);
    }
}

class Task extends TimerTask {

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j <= i; j++) {
                System.out.print("哈");
            }
            System.out.println("!!!");
        }
    }
}
quartz 任务进度管理器

scheduler - 调度器,控制所有调度
trigger - 触发器,采用DSL模式
JobDetail - 需要处理的JOB
Job - 执行逻辑

  • DSL - Domain specific language领域特定语言,针对一个特定领域,具有受限表达性的一种计算机语言程序,即领域专用语言,声明式编程(简洁/连贯的代码使用,可以之间.方法,类似.append().append())
HappenBefore 指令重排规则

程序代码执行的顺序与编写的代码不一致,即虚拟机优化代码顺序,优化程序性能

  • 在虚拟机层面,为了尽可能的减少内存操作速度远慢于cpu运行速度带来的cpu空置影响,虚拟机会按照自己的一些规则,将程序编写顺序重排,优先执行没有逻辑顺序的代码,提高性能
volatile

volatile是一个类型修饰符(type specifier).volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值
保证线程间变量的可见性,即当线程A对变量o修改后,线程A后面执行的其他线程能看到变量o的变动,即需要符合以下规则:

  • 线程对变量进行修改后,要立刻回写到主内存
  • 线程对变量读取的时候,要从主内存中读取,而不是缓存中
    volatile保证数据的可见性,但不能保证原子性
可重入锁

锁可连续使用 - 锁作为并发共享数据保证一致性的工具,大多数内置锁都是可重入的,也就是说,如果某个线程试图获取一个已经由它自己持有的锁时,那么这个请求会立刻成功,并且会将这个锁的计数值加1,而当线程退出同步锁代码块时,计数器将会递减,当计数值等于0时,锁释放。如果没有可重入锁的支持,在第二次企图获得锁时将会进入死锁状态。

CAS

悲观锁:synchronized-独占锁/悲观锁,会导致其他所有需要锁的线程挂起,等待持有锁的线程释放锁。
乐观锁:每次不加锁而是假设没有冲突而去完成某项操作,如果因冲突失败就重试,直到成功为止。 - 有当前内存值、原值、更新的值,若内存值和原值相同,即修改值并返回true,否则不做操作,返回false
CAS是一组原子操作,不会被外部打断,其属于硬件级别的操作,效率比加锁操作高,是利用cpu的cas指令,同时借助JNI来完成的非阻塞算法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值