Java基础——多线程简述

开启多线程的两种方法

继承Thread类

  • 子类继承Thread类(重写run()方法)
  • 启动线程:子类对象.start()
  • 由于Java是单继承,不建议使用继承Thread的方法进行实现多线程

实现Runnable接口

  • 实现Runnable接口(重写run()方法)

  • 启动线程:将该接口的实现类(你正在使用的类)作为参数传入Thread对象后,使用Thread对象.start()进行启动

  • 避免了Java的单继承性,方便同一个对象被多个线程使用的情况

    示例:

    public class Test1 implements Runnable{
        @Override
        public void run(){
            //方法体
        }
    }
    public static void main(String[] args){
        //创建Runnable实现类
        Test1 t1 = new Test1();
        
        //创建线程对象,通过线程对象开启线程,代理
        new Thread(t1).start();
        
        //主函数方法体
    }
    

实现Callable接口

  • 实现callable接口,需要返回值类型
  • 重写call方法,需要处理异常 返回值为 E
  • 创建目标对象
  • 创建执行服务:ExeutorService ES = Executors.newFixedThreadPool(int); int为线程池的大小
  • 提交执行:Future<E> result = ES.submit(目标对象);
  • 获取结果:E r = result.get()
  • 关闭服务:ES.shutdownNow()

线程的其他方法

线程的状态 Thread.State

  • 枚举类型
  • getState()方法,返回Thread.State 类型
Thread.State说明
NEW尚未启动的线程处于此状态。
RUNNABLE在Java虚拟机中执行的线程处于此状态。
BLOCKED被阻塞等待监视器锁定的线程处于此状态。
WAITING正在等待另一个线程执行特定动作的线程处于此状态。
TIMED_WAITING正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
TERMINATED已退出的线程处于此状态。

改变线程优先级 Priority

  • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。

  • 优先级用数字表示,从1~10

    ◆ Thread.MIN_ PRIORITY = 1;
    ◆ Thread.MAX_ PRIORITY = 10;
    ◆ Thread.NORM_ _PRIORITY= 5;

  • getPriority() 获取优先级‘; setPriority(int x)修改优先级

线程休眠 sleep

  • sleep(时间 ms)指定当前线程阻塞的毫秒数
  • 需要处理异常
  • 休眠时间到达后线程进入就绪状态
  • 可用于模拟延时、倒计时、方便检测多线程错误
  • 每个对象都有一个锁,sleep不会释放锁

线程停止

  • 不推荐使用jdk推荐的 stop() destroy()方法
  • 推荐让线程自己停下来(跑完次数)
  • 建议使用标志位进行终止线程 (用flag = false来结束run方法中的while(ture)循环)

线程礼让 yield

  • yield() 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 将线程从运行态转化成就绪态
  • 礼让不一定成功

线程的强制执行 join

  • join() 优先执行该线程,等此线程执行完后才能执行其他线程,此时其他线程阻塞

其他线程

守护线程和用户线程 Deamon

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户现场执行完毕
  • 虚拟机不必等待守护线程执行完毕
  • 用户线程: mian线程
  • 守护线程:gc线程(垃圾回收)、操作日志、内存监控等
  • 设置守护线程:thread.setDaemon(true) 默认是false的用户线程
  • 注: 必须先设置成守护线程后才能启动线程

线程同步 Synchronized

  • 由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制
    synchronized
  • 当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可.
  • 存在以下问题:
    ◆一个线程持有锁会导致其他所有需要此锁的线程挂起;
    ◆在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
    ◆如果一个优先级高的线程等待-个优先级低的线程释放锁 会导致优先级倒置,引起性能问题

使用方式

  1. synchronized方法
public synchronized void method(int args){
    //代码部分
}

synchronized方法控制对“对象”的访问, 每个对象对应一 把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞;

方法一旦执行,就独占该锁.直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁:,继续执行

  1. synchronized 块
public void method(int args){
    synchronized(线程对象){
        
    }
}

◆同步块: synchronized (Obj ){}
◆Obj称之为同步监视器
◆Obj 可以是任何对象,但是推荐使用共享资源作为同步监视器
◆同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this ,就是这个对象本身,或者是class

◆同步监视器的执行过程

  1. 第一个线程访问,锁定同步监视器,执行其中代码.
  2. 第二个线程访问 ,发现同步监视器被锁定,无法访问.
  3. 第一个线程访问完毕 ,解锁同步监视器.
  4. 第二个线程访问, 发现同步监视器没有锁,然后锁定并访问

线程通信

这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件.
◆对于生产者,没有生产产品之前,要通知消费者等待.而生产了产品之后,又需要马上通知消费者消费
◆对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费
◆在生产者消费者问题中,仅有synchronized是不够的
◆synchronized可阻止并发更新同一个共享资源,实现了同步
◆synchronized不能用来实现不同线程之间的消息传递(通信)

通信方法:

Object类提供了几个方法,但此方法只能在同步方法或者同步代码块中使用,否则会抛出IllegalMonitorStateException

方法名作用
wait()表示线程一直等待,直到其他线程通知,与sleep不同,此方法会释放锁
wait(long timeout)指定等待的毫秒数
notify()唤醒一个处于等待状态的线程
notifyAll()唤醒同一个对象上所有调用了wait方法的线程,优先级别高的线程优先调度

线程池

背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影
响很大。
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。
可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
好处:.

  • 提高响应速度(减少了创建新线程的时间)

  • 降低资源消耗(重复利用线程池中线程,不需要每次都创建)

  • 便于线程管理(…)

名称说明
corePoolSize核心池的大小
maximumPoolSize最大线程数
keepAliveTime线程没有任务时最多保持多长时间后会终止
unit对应上面存活时间的时间单位
workQueue阻塞队列

常用的线程池实现类

newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

这里我们可以看到,

  • 核心线程池数量为 0,也就是它不会永久保留任何线程;
  • 最大容量是 Integer.MAX_VALUE
  • 每个线程的存活时间是 60 秒,也就是如果 1 分钟没有用这个线程就被回收了;
  • 最后用到了同步队列。
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

这个线程池的特点是:

  1. 线程池中的线程数量是固定的,也是我们创建线程池时需要穿入的参数;
  2. 超出这个数量的线程就需要在队列中等待。
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

这个线程池顾名思义,里面只有 1 个线程。

演示使用

//1.创建服务,创建线程池
	ExecutorService ES = Executors.newFixedThreadPool(int nubs);
//2.执行
	ES.execute(new MyThread());
//3.关闭连接池
	ES.shutdown();

资料来源于网络

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值