Java进阶之线程

Java进阶之线程

Java进阶之线程

线程的概念

  • 可以同步一起执行
  • 线程和进程
    • 一个进程就是一个应用,而线程是在进程内部衍生出来的
    • 也可以说,线程是进程的最小组成单元
  • 并行和并发
    • 并行意味着一起开启
    • 并发意味着不但一起开启,同时CPU同时进行处理
    • 并行是并发产生的前序条件
    • 并行一般不会对机器产生较大压力,但是并发会产生大量压力
    • 降低线程的并发 RabbitMQ
      • 程序执行时间比较长--有效优化程序的执行时长(拆解)eg:消息队列
      • 底层一些机制 --排队机制
  • 同步和异步
    • 同步是指发送一个请求,需要等待返回后才能发送下一个请求,有等待过程
    • 异步是指发送一个请求后,不需要等待返回,随时可以发送下一个请求,即不需要等待
    • 同步是用于确保资源一次只能被一个线程使用,同步相比非同步,性能会相差三到四倍

线程运行机制

  • 就绪状态(新生)
  • 执行状态(执行)
  • 挂起状态
    • 阻塞
    • 等待
    • 超时等待
  • 死亡状态(终止)
  • 每次在执行java.exe时,都会有一个java虚拟机进程启动,执行程序带代码的任务是由线程来完成的,所以,每一个线程都一个独立的程序计数器 和方法调用栈
    • 程序计数器:也称为PC寄存器,当线程执行一个方法时,程序计数器会指向方法区中的下一条要执行的字节码指令
    • 方法调用栈:简称方法栈,用来跟踪线程运行中一系列的方法调用过程
  • 每当用Java命令启动一个Java虚拟机进程时,Java虚拟机都会创建一个主线程,该线程也就是main,也就是诚如的入口

使用线程

    • 继承Thread类,重写run方法
    • 优点:代码简单。
    • 缺点:无法继承别的类
public class ExtendsThread extends Thread{
    private String name;
    public ExtendsThread(String name){
        this.name = name;
    }
    public void run(){
        for(int i = 1;i<=100;i++){
            System.out.println(name+"跑了"+i+"米");
        }
    }

    public static void main(String[] args) {
        ExtendsThread extendsThread1 = new ExtendsThread("李麒麟");
        ExtendsThread extendsThread2 = new ExtendsThread("吉祥物");
        ExtendsThread extendsThread3 = new ExtendsThread("神兽");
        extendsThread1.start();
        extendsThread2.start();
        extendsThread3.start();
    }
}
    • 实现Runnable接口,实现run方法
    • 优点:可以继承其他类,实现同一接口的多个线程对象共享一个任务类对象,即多线程共享一份资源
    • 缺点:代码复杂,访问当前线程必须要使用Thread.currentThread()方法
public class ImplementsRunnable implements Runnable{
    private String name;
    public ImplementsRunnable(String name){
        this.name = name;
    }
    @Override
    public void run() {
        for(int i = 1;i<=100;i++){
            System.out.println(name+"跑了"+i+"米");
        }
    }

    public static void main(String[] args) {
        ImplementsRunnable implementsRunnable1 = new ImplementsRunnable("起名字真是太难了");
        ImplementsRunnable implementsRunnable2 = new ImplementsRunnable("起名字真是超级难");
        ImplementsRunnable implementsRunnable3 = new ImplementsRunnable("起名字真是难爆了");
        Thread thread1 = new Thread(implementsRunnable1);
        Thread thread2 = new Thread(implementsRunnable2);
        Thread thread3 = new Thread(implementsRunnable3);
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
    • 实现Callable接口
    • 优点:可以继承其他类,多线程共享一份资源,还具有返回值,可以跑出返回值的异常
    • 缺点:代码复杂,访问当前线程必须要使用Thread.currentThread()方法
    • 当需要调用get()方法时,如果线程还未执行完毕,则会阻塞到线程执行完毕后拿到返回值
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ImplementsCallable implements Callable {
    @Override
    public Integer call() throws Exception {
        int i = 0;
        for (i = 0;i<20;i++){
            if(i == 10){
                break;
            }
        }
        return i;
    }

    public static void main(String[] args) {
        ImplementsCallable implementsCallable = new ImplementsCallable();
        FutureTask futureTask = new FutureTask<>(implementsCallable);
        Thread thread1 = new Thread(futureTask,"线程1");
        thread1.start();
        try {
            System.out.println(Thread.currentThread().getName()+" "+futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
  • 常用方法
    • 获取线程名字:getName()
    • 获取线程标识:getId()
    • 获取线程优先级:getPriority()
    • 更改线程优先级:setPriority()
    • 判断线程是否存活:isAlive()
    • 设置线程为守护线程:setDaemon(true)
    • 判断线程是否为守护线程:isDaemon()
    • 设定线程名字:setName()
    • 让线程进入阻塞状态:sleep()
    • 启动线程:start()
    • 线程体:run()
    • join(),当前执行的线程调用其他线程,让自己进入阻塞状态,当其他线程执行完毕,自己再继续执行
    • sleep()方法进入阻塞状态后,在执行时不会释放对象锁,如果被join()方法中断,则会抛出异常,同时清除中断状态
    • yield(),一般用于测试,提高线程并发效果,但执行完毕后会让线程优先级高的,或同等级的线程优先执行

线程锁

    • 生产消费者模型
      • 线程AB共享固定大小缓冲区
      • 生产者:A产生数据存放到缓冲区
      • 消费者:B从缓冲区读取数据计算



      • 在多线程中,如果生产者生产数据的速度过快,导致消费者数据无法快速使用完毕,生产者就必须等待消费者消费完毕数据后,再次生产数据,为了生产消费达到一定的动态平衡,就需要一个缓冲区存放生产者的数据,这就是所谓的生产者-消费者模式
      • 特点
      • 保证生产者在缓冲区已满的时候不会再向缓冲区内写入数据,消费者也不会在缓冲区为空的时候消费数据
      • 当缓冲区满时,生产者会进入休眠,当消费者消耗缓冲区数据后,缓冲区有空余空间,生产者会被唤醒,继续往缓冲区中写入数据,同理,当缓冲区为空时,消费者会进入休眠,当生产者将数据写入缓冲区后,消费者会被唤醒,消费缓冲区内的数据进行计算。
    • 锁机制
      • 互斥锁synchronized --隐式锁
        • 对象锁synchronized("锁A")
        • 类锁synchronized(A.class) = public synchronized static void A(){}
        • synchronized是关键字,由虚拟机自动释放
        • 加锁方式:方法锁
public synchronized void sy(){
    //将方法加锁
}
        • 加锁方式:锁块
public class TestTrainTickets {
    static class TrainTickets implements Runnable{
        int trainTickents = 100;
        @Override
        public void run(){
            while (trainTickents>0){
                synchronized (this){
                    if(trainTickents<=0){
                        System.out.println(Thread.currentThread().getName()+"没票了,请关注下次列车");
                        break;
                    }else{
                        System.out.println(Thread.currentThread().getName()+"出售车票第"+(100-trainTickents+1)+"张");
                        trainTickents--;
                    }
                    try {
                        Thread.sleep(new Random().nextInt(500)+1);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        TrainTickets trainTickets = new TrainTickets();
        Thread thread1 = new Thread(trainTickets,"一号窗口");
        Thread thread2 = new Thread(trainTickets,"二号窗口");
        Thread thread3 = new Thread(trainTickets,"三号窗口");
        Thread thread4 = new Thread(trainTickets,"四号窗口");
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}
      • 重入锁ReentrantLock 显示锁
        • ReentrantLoct默认为非公平锁,可以通过构造器更改

                    

你可能感兴趣的:(Java进阶之线程)