JAVA多线程基础

本文详细介绍了Java中的多线程编程,包括线程的生命周期、状态转换、线程优先级、创建线程的三种方式,以及同步方法和同步块的概念。还讨论了线程礼让、线程加队、线程守护及死锁问题,并引入了Lock锁和线程池的概念,最后举例展示了如何使用ExecutorService创建线程池。
摘要由CSDN通过智能技术生成

Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务

多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。

这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。

多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。

一个线程的生命周期:

  • 新建状态:
    • 使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
  • 就绪状态:
    • 当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
  • 运行状态:
    • 如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
  • 阻塞状态:
    • 如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
      • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
      • 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
      • 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
  • 死亡状态:
    • 一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

线程的优先级

每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。

Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。

默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。

具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。

创建一个线程

Java 提供了三种创建线程的方法:

  • 通过继承 Thread 类本身;
  • 通过实现 Runnable 接口;
  • 通过 Callable 和 Future 创建线程

通过继承 Thread 类本身;

继承Thread类,重写run()方法,调用start()方法开启线程

//
public class MyThraed extends Thread{




    @Override
    public void run() {
        //run方法线程体;
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码"+i);
        }
    }


    public static void main(String[] args) {
        MyThraed thraed = new MyThraed();
        thraed.start();


        for (int i =0;i<100;i++){
            System.out.println("我在学习多线程"+i);
        }
    }
}

通过实现Runnable接口:

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        //run方法线程体;
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码"+i);
        }
    }


    public static void main(String[] args) {
        //启动线程
        MyRunnable runnable = new MyRunnable();
        new Thread(runnable).start();


        for (int i =0;i<100;i++){
            System.out.println("我在学习多线程"+i);
        }
    }
}

龟兔赛跑的例子"

	package com.hnh.Runnable;
//龟兔赛跑
public class Race implements Runnable{
    private String winner;
    @Override
    public void run() {
        for (int i=1;i<=100;i++){
            if (Thread.currentThread().getName().equals("兔子")&&i%10==0){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }


            if(raceisEnd(i)){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
        }


    }


    public boolean raceisEnd( int i){


        if(winner!=null){
            return true;
        }else {
            if (i==100){
                winner=Thread.currentThread().getName();
                System.out.println(winner+"获胜了");
                return true;
            }
        }


        return false;
    }


    public static void main(String[] args) {
        Race race = new Race();
        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
}

Callable接口

实现即可,需要开启服务,然后提交线程.

Lamda表达式:

函数式接口:

任何接口 如果只包含一个抽象方法,那么就是一个函数方式的接口,对于函数接口,我们可以直接使用Lamda的表达式来创建该接口的对象,

public class testLamda {
    public static void main(String[] args) {


        ILike like =  (int a)->{
            System.out.println("Lamda表达式");
        };
        like.Lamda();
        //(1)简化: 标识符
          ILike like =  (a)->{
            System.out.println("Lamda表达式");
        };
      
        //(2)简化括号
          ILike like =  a->{
            System.out.println("Lamda表达式");
        };
        //(3)简化花括号  有多行代码时不能简化
           ILike like =  a->System.out.println("Lamda表达式");
        
          
    }
}


interface ILike{
    void Lamda(int a);
}

生命周期:

    • NEW
    • 尚未启动的线程处于此状态。
    • RUNNABLE
    • 在Java虚拟机中执行的线程处于此状态。
    • BLOCKED
    • 被阻塞等待监视器锁定的线程处于此状态。
    • WAITING
    • 正在等待另一个线程执行特定动作的线程处于此状态。
    • TIMED_WAITING
    • 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
    • TERMINATED
    • 已退出的线程处于此状态。
  • 一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。

线程休眠Sleep()方法

模拟网络延时: 可以放大问题的发生性:

sleep不会释放锁.

线程礼让 yield():

礼让不一定成功,看cpu心情.

public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();




    }
}


class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程执行");
        Thread.yield(); //礼让
        System.out.println(Thread.currentThread().getName()+"线程结束");
    }
}

运行结果 礼让成功:

a线程执行

b线程执行

b线程结束

a线程结束

线程加队join():

	public class TestJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 500; i++) {
            System.out.println("线程vip来了"+i);
        }
    }


    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();
        for (int i = 1; i < 1000; i++) {
            if(i==200){
                thread.join();
            }
            System.out.println("main"+i);
        }
    }
}

线程优先级:

getPriority(), setPriority(), 设置线程的优先级.

守护线程daemon:

虚拟机必须保证用户线程执行完比,而不用等待守护线程完成,

操作日志,监控内存,垃圾回收等守护线程

通过SetDaemon(true)来设置守护线程 ,正常线程 默认false 都是用户线程

同步方法synchronized:

包括同步方法和同步块:

同步方法:

public synchronized void buy(){
    
}

同步块:()里面的是加锁对象

synchronized (account){


    if (account.money-drawmoney<=0){
        System.out.println(Thread.currentThread().getName()+"钱不够了,取不了");
        return;
    }


    account.money = account.money-drawmoney;
    nowMoney=nowMoney+drawmoney;
    System.out.println(account.name+"余额"+account.money);
    System.out.println(this.getName()+"手里的钱"+nowMoney);
}

死锁:

某一个同步代码块,同时拥有俩个以上对象的锁时,就可能发生 死锁问题.

synchronized (a){
    System.out.println(geta);
    synchronized (b){
        System.out.println(getb);
    }
}

改进:

synchronized (a){
    System.out.println(geta);
}
 synchronized (b){
        System.out.println(getb);
}

Lock锁:

ReentranLock:

实现了Lock,可重入锁:可以显示加锁,和释放锁.

public class Lock implements Runnable{


    private int ticks = 10;
    private ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {


        while (true){
            try {
                lock.lock();
                if (ticks<=0){
                    break;
                }
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"抢到了第"+ticks--+"票");
            } finally {
                lock.unlock();
            }


        }
    }


    public static void main(String[] args) {
        Lock thread4 = new Lock();
        new Thread(thread4,"小明").start();
        new Thread(thread4,"老师").start();
        new Thread(thread4,"黄牛").start();
    }
}

消费者与生产者问题的实现:

需要方法 wait(), 和方法 notifyAll(),

wait(): 使当前线程进入等待,并且放弃锁资源,

notifyAll(): 唤醒所有当前正在等待的线程,

管程:

信号灯法:

线程池:

提前创建好多个线程,放到一个池子里,使用完放回,避免频繁的创建和销毁,

ExecutorService和Executors

public class testPool {
    public static void main(String[] args) {
        //1.创建服务创建线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        //执行
        service.execute(new myThead());
        service.execute(new myThead());
        service.execute(new myThead());
        service.execute(new myThead());
        //关闭链接
        service.shutdown();
    }
}
class myThead implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值