线程

Thread:

jvm虚拟机的启动是单线程的还是多线程的?

JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。 

线程有两种调度模型: 

 

分时调度模型   

所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片 

抢占式调度模型   

优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。  

Java使用的是抢占式调度模型。 

多线程的实现

方案一:

继承Thread类 ,重写run()方法,创建自定义对象,调用start();

 

方案二:(一般使用方式二)

实现runnable接口,重写run()方法,

创建该对象,并且

通过Thread的代参构造,

public Thread(runnble r); 创建线程对象。

调用start()方法

为什么要重写run()方法?

run(),里面 封装的是被线程执行的代码。

run()和start()方法的区别 

run():仅仅是封装被线程执行的代码,直接调用是普通方法

start():首先启动了线程,然后再由JVM去调用该线程的run()方法。

实现接口方式的好处 

可以避免由于Java单继承带来的局限性。 

适合多个相同程序的代码去处理同一个资源的情况,

把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。 

Thread类的基本获取和设置方法 

 

public final String getName() 

public final void setName(String name) 

其实通过构造方法也可以给线程起名字 

思考: 如何获取main方法所在的线程名称呢? 

public static Thread currentThread() 

这样就可以获取任意方法所在的线程名称 

如何设置和获取线程优先级 

 

public final int getPriority() 

public final void setPriority(int newPriority)  :传递一个int值,范围1-10之间。

优先级:1-10依次整大。1优先级最低,10优先级最高,默认为5。优先级越高,抢到的cpu概率就越大,但是要在多次运行的时候,才能看到比较好的效果。

线程状态:

线程休眠 

public static void sleep(long millis) 

线程加入 

public final void join() :为了让该线程走完了,其他线程加入。等待该Thread线程终止时间为Millis,如果Millis时间之后还未终止,则执行下面代码。//如果已经终止了直接就执行。等待该线程终止的时间最长为 millis 毫秒。超时为 0 意味着要一直等下去。

线程礼让 

public static void yield() :暂停当前线程,并执行其他线程,保证多线程执行更和谐,但是并不保证绝对公平。

后台线程 

public final void setDaemon(boolean on) :将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。该方法首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。

中断线程 

public final void stop() :让线程停止,已经过时,不建议使用。

public void interrupt() :中断线程,把线程的状态终止,并且抛出一个InterruptedException;

面试题:线程的生命周期?

线程安全问题:

 

相同的票出现多次 

CPU的一次操作必须是原子性的 

还出现了负数的票 

随机性和延迟导致的 

解决线程安全问题的基本思想 :

首先想为什么出现问题?(也是我们判断是否有问题的标准) 

是否是多线程环境 

是否有共享数据 

是否有多条语句操作共享数据 

如何解决多线程安全问题呢? 

基本思想:让程序没有安全问题的环境。 怎么实现呢? 

把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。 

解决线程安全问题实现1

同步代码块 

格式:         

synchronized(对象){需要同步的代码;} 

同步可以解决安全问题的根本原因就在那个对象上。

锁对象必须是同一个对象

该对象如同锁的功能。 

同步代码块的对象可以是哪些呢? 

任意对象

同步的特点:

同步的前提 

多个线程 

多个线程使用的是同一个锁对象 

同步的好处 

同步的出现解决了多线程的安全问题。 

同步的弊端 

当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。 

解决线程安全问题实现2 

同步方法 

 

就是把同步关键字synchronized加到方法上 

同步方法锁对象:

同步方法的锁对象是方法内部的this,不需要明确写出

如果是静态方法,同步方法的锁对象是当前类的class文件对象 obj.class

那么,我们到底使用谁? 

如果锁对象是this,就可以考虑使用同步方法。 

否则能使用同步代码块的尽量使用同步代码块。 

为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象

Lock接口 

Lock void lock() 

void unlock() 

使用子类ReentrantLock 创建锁对象。

同步弊端 

 

效率低 

如果出现了同步嵌套,就容易产生死锁问题 

死锁问题及其代码 

 

是指两个或者两个以上的线程在执行的过程中,

因争夺资源产生的一种互相等待现象

Java等待唤醒机制

(让生产者,消费者模式的线程一进一出均匀分配)

object类中提供了三个方法:

wait():等待

notify():唤醒单个线程

notifyAll():唤醒所有线程

注意:wait()等待执行的时候,立刻释放锁。

唤醒的时候,从等待的地方执行。

唤醒之后,继续抢夺CPU资源。

为什么这些方法不定义在Thread类中呢?

因为这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象,

所以,这些方法定义在object类中。

线程状态转换图以及常见执行情况:

 

Java线程组

Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。 

默认情况下,所有的线程都属于主线程组(main线程组)。 

public final ThreadGroup getThreadGroup() 

可以通过ThreadGroup 里面的getName(),获取线程组的名称。

我们也可以给线程设置分组 

Thread(ThreadGroup group, Runnable target, String name)

Java线程池:

JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法 

public static ExecutorService newCachedThreadPool()     

创建一个具有缓存功能的线程池     

缓存:百度浏览过的信息再次访问

public static ExecutorService newFixedThreadPool(int nThreads)     

创建一个可重用的,具有固定线程数的线程池

public static ExecutorService newSingleThreadExecutor() 

创建一个只有单线程的线程池,相当于上个方法的参数是1    

这些方法的返回值是ExecutorService对象,该对象表示一个线程池,

可以执行Runnable对象或者Callable对象代表的线程。

ExecutorService提供了如下方法 :

Future<?> submit(Runnable task) 

<T> Future<T> submit(Callable<T> task) 

方法步骤:

创建线程池对象 

创建Runnable实例 

提交Runnable实例 

关闭线程池 

多线程程序实现方案3

实现Callable接口 

步骤和刚才演示线程池执行Runnable对象的差不多。

匿名内部类方式使用多线程

匿名内部类方式使用多线程 

new Thread(){代码…}.start(); 

New Thread(new Runnable(){代码…}).start(); 

多线程常见的面试题:

1:多线程有几种实现方案,分别是哪几种?

    两种。

 

    继承Thread类

    实现Runnable接口

 

    扩展一种:实现Callable接口。这个得和线程池结合。

 

2:同步有几种方式,分别是什么?

    两种。

 

    同步代码块

    同步方法

 

3:启动一个线程是run()还是start()?它们的区别?

    start();

 

    run():封装了被线程执行的代码,直接调用仅仅是普通方法的调用

    start():启动线程,并由JVM自动调用run()方法

 

4:sleep()和wait()方法的区别

    sleep():必须指时间;不释放锁。

    wait():可以不指定时间,也可以指定时间;释放锁。

 

5:为什么wait(),notify(),notifyAll()等方法都定义在Object类中

    因为这些方法的调用是依赖于锁对象的,而同步代码块的锁对象是任意锁。

    而Object代码任意的对象,所以,定义在这里面。

 

6:线程的生命周期图

    新建 -- 就绪 -- 运行 -- 死亡

    新建 -- 就绪 -- 运行 -- 阻塞 -- 就绪 -- 运行 -- 死亡

    建议:画图解释。

 

线程状态:

新建:当一个线程产生

就绪:线程调用开始方法就进入就绪状态。

运行:当线程抢到cpu控制权,进入运行状态,执行自己的代码逻辑

等待:线程调用wait方法,进入等待状态,只有被notify之后,才会继续去抢。

睡眠:线程调用sleep方法之后,进入睡眠状态,当休眠时间结束之后,会自动唤醒,继续抢夺cpu控制权。

挂起:

阻塞:blocked,一般是等待io事件。

死亡:当代码逻辑执行完毕,线程死亡。

当一个程序开启时,cpu开启一个进程,为该进程产生一个主线程,和一个垃圾回收线程,当主线程结束的时候,整个程序就会结束。

线程的生成方式:

继承thread类。

实现runnable接口。

线程同步的方式:

synchronized方法:在方法的返回值前面上加Synchronized

synchronized修饰的代码块(对象锁):在需要同步的代码块上加synchronized(boject)关键字。

Lock:在需要同步的代码前使用lock()方法加锁,在结束同步的地方用unlock取消。

Thread常用方法:

Thread.start():开始一个线程.

Thread .sleep(long millis)在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)。

Thread .isAlive():测试线程是否处于活动状态。

 

Thread.join();等待该线程终止,如果一直未终止,当前线程将不会继续进行。

Thread .notify(),唤醒该线程。唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。

Thread .holdsLock(Object obj) 当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。

Thread .isDaemon()测试该线程是否为守护线程。

谈谈多线程:

线程的概念:

     线程是程序执行流的最小单元

   运行时产生一个或多个进程(process),而每一个进程又对应一至多个线程(thread),每一个线程又可以分为一至多个任务(task).

创建线程的两种实现方式:    

    1)继承Thread类

    2)实现Runnable接口

    

多线程的安全隐患以及同步机制

1)使用关键字“synchronized”对资源进行封锁,此方式称作“隐式加锁”;

2)采用java内部工具类“Lock”进行“显式加锁”。

 

高并发、分布式、高负载、缓存方面

    

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值