多线程基础知识

文章介绍了Java中实现多线程的三种方式:继承Thread类、实现Runnable接口以及实现Callable接口。详细讲解了线程的启动、成员方法,包括线程的生命周期、优先级、守护线程、线程安全问题,如同步代码块、同步方法和Lock锁。此外,还提到了生产者消费者模式下的阻塞队列以及线程池的概念,如ExecutorService和线程池的创建及自定义。

线程的三种启动方式

第一种线程启动方式

继承Tread类,重写run方法

public class MyThread extends Thread{
    @Override
    public void run() {
        //书写线程执行的代码
        for(int i=0; i<100; i++){
            System.out.println(getName() + "HelloWorld");
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
//        多线程的第一种启动方式:
        //1 自己定义一个类基础Thread
        //2 重写run方法
        //3 创建子类的对象,并启动线程

        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        //设置线程的名字
        t1.setName("线程一");
        t2.setName("线程二");

        //通过start启动线程
        t1.start();
        t2.start();

    }
}

第二种实现Runnable接口

public class MyThread implements Runnable{
    @Override
    public void run() {
        //书写线程执行的代码
        for(int i=0; i<100; i++){
            //getName()是Thread的方法,所以先把当前Tread找出
            System.out.println(Thread.currentThread().getName() + "HelloWorld");
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
//        多线程的第二种启动方式:
        //1 自己定义一个类实现Runnable接口
        //2 重写里面的run方法
        //3 创建自己类的对象
        //4 创建一个Thread类的对象,并开启线程

        //创建MyThread对象
        MyThread mt = new MyThread();
        //创建线程对象
        Thread t1 = new Thread(mt);
        Thread t2 = new Thread(mt);
        //设置线程的名字
        t1.setName("线程一");
        t2.setName("线程二");

        //通过start启动线程
        t1.start();
        t2.start();

    }
}


第三种实现Callable接口(实现该接口的线程,可返回线程结果)

public class MyThread implements Runnable{
    @Override
    public void run() {
        //书写线程执行的代码
        for(int i=0; i<100; i++){
            //getName()是Thread的方法,所以先把当前Tread找出
            System.out.println(Thread.currentThread().getName() + "HelloWorld");
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
//        多线程的第二种启动方式:
        //1 自己定义一个类实现Runnable接口
        //2 重写里面的run方法
        //3 创建自己类的对象
        //4 创建一个Thread类的对象,并开启线程

        //创建MyThread对象
        MyThread mt = new MyThread();
        //创建线程对象
        Thread t1 = new Thread(mt);
        Thread t2 = new Thread(mt);
        //设置线程的名字
        t1.setName("线程一");
        t2.setName("线程二");

        //通过start启动线程
        t1.start();
        t2.start();

    }
}


常见的成员方法

String getName() 返回此线程的名称

void setName() 设置线程的名字(构造方法也可以设置名字)
构造方法指的是 Thread的构造方法
继承类需要写自己的构造类

  public MyThread(String name){
    super(name);
  }

这样设置即可构造方法设置名字

static Thread currentThread( ) 获取当前线程的对象
细节:当JVM虚拟机启动之后,会自动的启动多条线程,其中一条线程就叫做main线程,她的作用就是去调用main方法,并执行里面的代码

static void sleep(long time) 休眠线程,单位为毫秒

线程的优先级 0-10 默认5

sePriority()

Thread t1 = new Thread()
t1.sePriority();

虽然优先级越高,执行的顺序越前,但也不是绝对,只是概率越高

线程的调度

1、抢占式调度(自由度高)随机 java的选择
2、非抢占式调度

守护线程

final void setDaemon(boolean on)
当其他的非守护线程执行完毕之后,守护线程会陆续结束

MyThread1 t1 = new MyThread1();
MyThread2 t2 = new MyThread2();

t2.setDaemon(true);
t1.start();
t2.start();

t1为非守护线程,则当t1结束时,t2也会跟着结束

礼让线程和插入线程

礼让线程(public static void yield)写在重写线程类中的run方法

   public class MyThread extends Thread{
       @Override
       public void run(){
          for(int i=1;i<=100;i++){
          System.out.println(getName() + i);
          //表示出让当前CPU的执行权
          Thread.yield();
          }
       }
   }

插入线程public final void join( )

MyThread t= new MyThread();
t.join();

线程的生命周期

就是计算机系统线程的五大周期

创建   就绪     执行     结束
           阻塞

线程的安全问题

同步代码块

//obj一定要唯一,则一般传入的是当前类的字节码对象 例如该类为MyThread,则obj = MyThread.class
synchronized( obj ){

}
则其中的代码边上锁了,同一时间只有一个用户运行执行,synchronized是自动上锁、自动解锁
细节:1、一定要写在循环里面synchronized( obj );
2、obj一定要是唯一的,不唯一就相当于有多把锁,则无法对代码块进行限制

 public class MyThread extends Thread{
    //加上static后,表示这个类所有的对象,都共享ticket数据(静态对象)
    static int ticket = 0;//0~99
    //锁对象,一定要是唯一的(则加上static修饰符)
    static Object obj = new Object();

    @Override
    public void run(){
       while(true){
          //同步代码块
          synchronized(obj){
               ....
          }
       }
    }

 }

同步方法(StringBuilder就是线程不安全,StringBuffer线程安全)

修饰符 synchronized 返回值类型 方法名(方法参数){…}
特点1:同步方法是锁住方法里面的所有代码
特点2:锁对象不能自己指定,而是由java指定 非静态方法时: this
静态方法时: 当前类的字节码文件对象

书写方法:1.循环 2.写同步代码块 3.把同步代码块提取为方法

lock锁,lock锁可以自行决定何时上锁、何时解锁

//Lock是一个接口,所有创建对象时,要用new它的实现类ReentrantLock
//使用第一种方式创建线程时,一定要给这样 修饰 static
static Lock lock = new ReentrantLock();
@Override
public void run(){
    while(){
    lock.lock();
         try{
           ...
         }catch(){
            ...
         }finally{
           lock.unlock();
         }    
    }
}

生产者消费者(常见方法)

pv方式

void wait() 当前线程等待,直到被其他线程唤醒
void notify() 随机唤醒单个线程
void notifyAll() 唤醒所有线程

在资源类中定义锁对象,然后synchronized(锁对象),然后由锁对象来使用wait()或notify()
就是计算机操作系统的pv操作,找一个类定义好资源,定义好锁对象,然后生产者消费者之间相互PV

等待队列 (不用我们去控制pv操作了)

阻塞队列的继承结构

 Iterable
    ↓
 Collection              接口继承接口  4个
    ↓
 Queue
    ↓
 BlockingQueue   

ArrayBlockingQueue         LinkedBlockingQueue    两个实现类
   底层数组,有界                底层链表,无界

Cook

public class Cook extends Thread(){
    ArrayBlockingQueue<String> queue;
    
    //透过构造方法才能使生产者和消费者用到一个队列,在main方法中定义时传入
    public Cook(ArrayBlockingQueue<String> queue){
           this.queue = queue;
}
   @Override
   public void run(){
     while(true){
     try{
     //不断放面条,只要队列未满则可一直放,满了则阻塞
          queue.put("面条");
      }catch(InterruptedException e){
         e.printStackTrace();
      }
     }
   }
   
}

Foodie

public class Foodie extends Thread(){
    ArrayBlockingQueue<String> queue;
    
    //透过构造方法才能使生产者和消费者用到一个队列,在main方法中定义时传入
    public Cook(ArrayBlockingQueue<String> queue){
           this.queue = queue;
}
   @Override
   public void run(){
     while(true){
     try{
     //不断吃面条,只要队列为空则阻塞
          queue.take("面条");
      }catch(InterruptedException e){
         e.printStackTrace();
      }
     }
   }
   
}

Main

public class ThreadDemo{
   public static void main(String[] args){
        //生产者消费者一定要在同一阻塞队列中
     //底层为数组的阻塞队列,要自己设置大小
      ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
     
     //创建线程并传递队列
     Cook c = new Cook(queue);
     Foodie f  = new Foodie(queue);
   c.start();
   f.start();
   }
}

多线程的6种状态

新建状态(NEW) --> 创建线程对象

就绪状态(RUNNABLE) --> start方法

阻塞状态(BLOCKED) --> 无法获得锁对象

等待状态(WAITING) --> wait方法

计时等待(TIMED_WATING) --> SLEEP方法

结束状态(TERMINATED) --> 全部代码运行完毕

线程池

线程池主要就是为了能够重复利用线程,一般创建个线程用完后,不销毁,放入线程池中即可。
Executors: 线程池的工具类通过调用方法返回不同类型的线程池对象。

方法名称
public static ExecutorService newCachedThreadPool() 创建一个没有上限的线程池(假没有上限,上限为int)
public static ExecutorService newFixedThreadPool(int nThreads) 创建有上限的线程池

public class MyThreadPoolDemo {
    public static void main(String[] args){
        //1.获取线程池对象(无上限版)
        ExecutorService pool1 = Executors.newCachedThreadPool();

        //2.提交任务
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());
        pool1.submit(new MyRunnable());

        //3.销毁线程池  但是我们一般是不销毁线程池的
        //pool1.shutdown();
    }
}

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for(int i=1;i<10;i++){
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}


自定义线程池

只有核心线程占满后,且队列也排队排满了,则才创建临时线程

public class MyThreadPoolDemo1{
在这里插入图片描述

}

请添加图片描述

任务拒绝策略(到核心线程、队列、临时线程全满后,开启该策略)

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值