我是靠谱客的博主 危机翅膀,最近开发中收集的这篇文章主要介绍【多线程核心技术】---Thread线程,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

进程和线程:

1)进程是静态的,其实就是指开启的一个程序;而线程是动态的,是真正执行的单元,执行的过程。其实我们平时看到的进程,是线程在执行着,因为线程是作为进程的一个单元存在的。

2)同样作为基本的执行单元,线程是划分得比进程更小的执行单位。

3)每个进程都有一段专用的内存区域。与此相反,线程却共享内存单元(包括代码和数据),通过共享的内存单元来实现数据交换、实时通信与必要的同步操作。

创建线程的方式:

创建方式一:继承Thread

    1:定义一个类继承Thread

    2:覆盖Thread中的run方法(将线程运行的代码放入run方法中)。

    3:直接创建Thread的子类对象

    4:调用start方法(内部调用了线程的任务(run方法));作用:启动线程,调用run方法

 

方式二:实现Runnable

    1:定义类实现Runnable接口

    2:覆盖Runnable接口中的run方法,将线程的任务代码封装到run中

    3:通过Thread类创建线程对象

4、并将Runnable接口的子类对象作为Thread类的构造函数参数进行传递

作为参数传递的原因是让线程对象明确要运行的run方法所属的对象。

区别:

       继承方式:线程代码放在Thread子类的run方法中

       实现方式:线程存放在接口的子类run方法中;避免了单继承的局限性,建议使用。

线程状态:

新建:start()

临时状态:具备cpu的执行资格,但是无执行权

运行状态:具备CPU的执行权,可执行

冻结状态:通过sleep或者wait使线程不具备执行资格,需要notify唤醒,并处于临时状态。

消亡状态:run方法结束或者中断了线程,使得线程死亡。

多线程安全问题

多个线程共享同一数据,当某一线程执行多条语句时,其他线程也执行进来,导致数据在某一语句上被多次修改,执行到下一语句时,导致错误数据的产生。

因素:多个线程操作共享数据;多条语句操作同一数据

解决:

       原理:某一时间只让某一线程执行完操作共享数据的所有语句。

       办法:使用锁机制:synchronized或lock对象

线程的同步:

当两个或两个以上的线程需要共享资源,他们需要某种方法来确定资源在某一刻仅被一个线程占用,达到此目的的过程叫做同步(synchronization)。

同步代码块:synchronized(对象){},将需要同步的代码放在大括号中,括号中的对象即为锁。

线程运行状态的几个方法:

1)start方法

start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。

2)run方法

run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。

3)sleep方法

sleep方法有两个重载版本:

sleep(long millis)     //参数为毫秒
sleep(long millis,int nanoseconds)    //第一参数为毫秒,第二个参数为纳秒

sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。

但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。

4)yield方法

调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。

注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。

5)join方法

join方法有三个重载版本:

join()
join(long millis)     //参数为毫秒
join(long millis,int nanoseconds)    //第一参数为毫秒,第二个参数为纳秒

假如在main线程中,调用thread.join方法,则main方法会等待thread线程执行完毕或者等待一定的时间。如果调用的是无参join方法,则等待thread执行完毕,如果调用的是指定了时间参数的join方法,则等待一定的事件。

wait方法会让线程进入阻塞状态,并且会释放线程占有的锁,并交出CPU执行权限。

由于wait方法会让线程释放对象锁,所以join方法同样会让线程释放对一个对象持有的锁。具体的wait方法使用在后面文章中给出。

6)interrupt方法

interrupt,顾名思义,即中断的意思。单独调用interrupt方法可以使得处于阻塞状态的线程抛出一个异常,也就说,它可以用来中断一个正处于阻塞状态的线程;另外,通过interrupt方法和isInterrupted()方法来停止正在运行的线程。

直接调用interrupt方法不能中断正在运行中的线程。

但是如果配合isInterrupted()能够中断正在运行的线程,因为调用interrupt方法相当于将中断标志位置为true,那么可以通过调用isInterrupted()判断中断标志是否被置位来中断线程的执行。

7)stop方法

stop方法已经是一个废弃的方法,它是一个不安全的方法。因为调用stop方法会直接终止run方法的调用,并且会抛出一个ThreadDeath错误,如果线程持有某个对象锁的话,会完全释放锁,导致对象状态不一致。所以stop方法基本是不会被用到的。

线程属性的几个方法:

1)getId

用来得到线程ID

2)getName和setName

用来得到或者设置线程名称。

3)getPriority和setPriority

用来获取和设置线程优先级。

4)setDaemon和isDaemon

用来设置线程是否成为守护线程和判断线程是否是守护线程。

守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。

Thread类有一个比较常用的静态方法currentThread()用来获取当前线程。

线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
      线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据

JAVA中有3种方式可以终止正在运行的线程

①线程正常退出,即run()方法执行完毕了

②使用Thread类中的stop()方法强行终止线程。但stop()和suspend(),resume()方法已经过期了,不推荐使用。调用stop()抛出异常ThreadDeath

③使用中断机制interrupt

中断机制

interrupted()方法与 isInterrupted()方法都是反映当前线程的是否处于中断状态的。

①interrupted()它测试的是当前线程(current thread)是否已经是中断状态,且这个方法会清除中断状态。(执行后将状态标志清除为false)

②isInterrupted()它测试线程(thread)对象是否已经是中断状态,方法不会清除中断状态。

interrupted()测试的是当前的线程的中断状态。而isInterrupted()测试的是调用该方法的对象所表示的线程。一个是静态方法(它测试的是当前线程的中断状态),一个是实例方法(它测试的是实例对象所表示的线程的中断状态)。

(1)interrupt()和return

(2)当线程正常运行时,在运行到B点时被调用了interrupt(), 此时该线程将继续正常运行,但isInterrupt的状态会被设为true,当做完 A,B,C的代码时,再进入while (!Thread.currentThread().isInterrupted()) 时,该线程被销毁。假如while的检查条件改为(true),该线程不会受影响,将会一直运行下去。

多线程的特性

1:多线程的优先级具有继承性

A线程启动B线程,A与B的优先级一样

2:多线程的优先级具有规则性

    优先级高的大部分先执行,但不能保证优先级高的全部先执行完

3:多线程的优先级具有随机性

    线程的优先级与运行结果的顺序无关


用户线程和守护线程的区别
用户线程和守护线程都是线程,区别是Java虚拟机在所有用户线程dead后,程序就会结束。而不管是否还有守护线程还在运行,若守护线程还在运行,则会马上结束。很好理解,守护线程是用来辅助用户线程的,如公司的保安和员工,各司其职,当员工都离开后,保安自然下班了。

用户线程和守护线程的适用场景
由两者的区别及dead时间点可知,守护线程不适合用于输入输出或计算等操作,因为用户线程执行完毕,程序就dead了,适用于辅助用户线程的场景,如JVM的垃圾回收,内存管理都是守护线程,还有就是在做数据库应用的时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监听连接个数、超时时间、状态等。

创建守护线程
调用线程对象的方法setDaemon(true),设置线程为守护线程。
1)thread.setDaemon(true)必须在thread.start()之前设置。
2)在Daemon线程中产生的新线程也是Daemon的。
3)不是所有的应用都可以分配给Daemon线程来进行服务,比如读写操作或者计算逻辑。因为Daemon Thread还没来得及进行操作,虚拟机可能已经退出了。



最后

以上就是危机翅膀为你收集整理的【多线程核心技术】---Thread线程的全部内容,希望文章能够帮你解决【多线程核心技术】---Thread线程所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(43)

评论列表共有 0 条评论

立即
投稿
返回
顶部