怎么创建Thread类

55次阅读
没有评论

共计 4415 个字符,预计需要花费 12 分钟才能阅读完成。

这篇文章主要介绍“怎么创建 Thread 类”,在日常操作中,相信很多人在怎么创建 Thread 类问题上存在疑惑,丸趣 TV 小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么创建 Thread 类”的疑惑有所帮助!接下来,请跟着丸趣 TV 小编一起来学习吧!

线程的基本概念

进程(Process)是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位。程序只是一组指令的有序集合,它本身没有任何运行的含义,只是一个静态实体。而进程则不同,它是程序在某个数据集上的执行,是一个动态实体。它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而被撤消,反映了一个程序在一定的数据集上运行的全部动态过程。

线程(Thread)是进程的一个实体,是 CPU 调度和分派的基本单位。线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

线程和进程的关系是:线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时该进程所产生的线程都会被强制退出并清除。线程可与属于同一进程的其它线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息 (如程序计数器、一组寄存器和栈)。

要理解多线程的概念,就要首先明白它所带来的好处,假设没有线程的概念存在,那么当一个应用程序在执行时,如果需要多个分支程序能够同时处理数据,就需要再开启一个进程,

例如:要想同时执行多个 JAVA 文件的 main 方法,那岂不是要启动多个 JAVA 虚拟机才可以?每个虚拟机都要为它分配一块内存,计数器、寄存器等等,这样消耗系统资源的话,操作系统是肯定不乐意的!为了能够同时运行多个分支程序又不至于大动干戈,所以才有了线程的概念,线程的开启相对来说是较容易的,它是在进程内完成的。操作系统不会再给它分配一块内存,数据区等,所以说一个进程当中的所有线程都是共享内存的。

线程之间的切换

由于现在的操作系统都是支持多进程和多线程的。(早先的电脑例如 DOS 系统是不支持多进程的,就是说同一时间只能有一个程序在运行)而 CPU 的数量永远是有限的,虽然随着硬件的发展,一台电脑的 CPU 数量变的越来越多,但永远也无法达到每一个程序分配一个 CPU 的程度。因此操作系统必须要将有限的 CPU 资源对应用程序进行合理的分配。也就是说,多个程序抢一个 CPU 的话,大家就要轮流执行,也就存在了进程之间的切换,同样多个线程之间也需要切换。线程之间的切换相对进程来说开销是比较小的。所以它被认为是轻量级的。

如何启动一个线程?两种方法

JDK 中提供了一个 Thread 类,它可以帮助我们在 java 程序中额外启动一个线程。为什么说是额外?因为 main 函数也是一个线程,它称之为程序的主线程。

启动一个线程,当然就需要创建 Thread 类的一个实例:

Thread t = new Thread(){

@Override

publicvoid run() {

// TODO Auto-generated method stub

}

};

—————————————————————-

Thread t = new Thread(new Runnable(){

@Override

publicvoid run() {

// TODO Auto-generated method stub

}

});

t.start();

Thread 类接收一个参数,一个 Runnable 类型的对象,查看 API 文档得知,Runnable 是一个接口,并且定义了一个 run()方法。

你只需要自定义一个类实现该接口,将你需要执行的代码,写在 run 方法里就可以了。除此以外,开启线程还有另外一个办法,那就是自定义一个类,继承 Thread 类。再观察 API 文档,我们看到 Thread 类本身也实现了 Runnable 接口,所以我们把 Thread 类中的 run 方法进行重写,也可以达到目的。

线程的 sleep() 方法

线程的睡眠,睡眠时不占用 CPU 资源,可以被 interrupt() 方法打断,打断后线程会抛出 InterruptedException,线程在睡眠的过程中,所拿到的同步锁是不会释放的。

线程的 join() 方法(合并线程)

当线程调用 join 方法时,该线程会与当前线程进行合并,即等待该线程执行完成,再继续执行,join 还有带参数的重载方法,可以指定等待多少毫秒。

若在 main 方法中执行 t.join(2000);  main 线程会等待线程 t 两秒钟,之后再运行。

线程的 yield() 方法 ( 手动切换线程)

该方法使线程让出 CPU 资源,由运行状态转为就绪状态。此时操作系统会重新为线程分配 CPU。并且 yield()方法只能让同优先级的线程有执行的机会。

yield 不会导致线程阻塞,所以无法保证一定能将 CPU 让给别的线程。假设某个线程的优先级别最高,此时调用 yield 方法,操作系统很有可能再次选中该线程并分配 CPU。就好像一个年龄最大的人让出自己最年长的称号让大家重新选出最年长者一样。这种假惺惺的高风亮节是不会获得大家好感的。

线程的状态和优先级

线程的优先级用数字来表示,从 1~10 表示优先级由低到高。操作系统在调度 CPU 的时候,会优先把 CPU 分配给优先级高的线程来运行。

线程的同步问题

银行取款,数据的并发操作

ATM 银行柜台数据库查询请求 1 返回结果 1 取款请求 1 查询请求 2 返回结果 2 取款请求 2

查询请求 1 和查询请求 2 都执行完毕的时候,假如数据库返回的结果 1 和结果 2 都是余额为 10000。此时银行柜台发出取款请求 1,取款 8000 元,由于余额 10000 8000,因此请求被允许,余额应剩余 2000。取款请求最终转换为 DATABASE 的更新操作,将余额更新为 2000. 在同一时刻,ATM 发出取款请求 2,取款 8000 元,由于余额 10000 8000,因此请求再次被允许,余额应剩余 2000。取款请求最终同样转换为数据库更新操作,将余额更新为 2000。这个时候就出现了一个严重的问题,共取走了 16000,数据库却还剩 2000。

这是一个非常经典的多线程引发的安全问题,为了解决这个问题,我们引入同步锁的概念。

同步锁的概念:所谓同步,指的是按步骤依次执行。如果是同时执行的操作,我们称为异步或者叫并发。当一个线程执行请求时,如果把数据库的一行记录锁定,其它线程此时不能操作数据库,必须等待第一个线程结束。这样就能避免同时操作一行记录所带来的问题。

线程安全问题:是指当多个线程访问同一个数据时,如果每个线程都对该数据做了修改,那么该数据的值就会变得难以确定。一个值不能确定的变量,有可能会引发整个系统产生灾难性的错误,所以这是我们绝不希望看到的。因此,解决线程安全问题,两种办法:

第一、如果一个类需要被多线程访问,那么绝对不要添加任何成员变量,防止多线程共享一份数据而引发的数据混乱。

第二、采用线程同步来访问同一个数据。

JAVA 为了解决线程安全问题,也引入了同步锁的机制: Synchronized 关键字

JVM 的规范中这样写道:

“在 JVM 中,每个对象和类在逻辑上都是和一个监视器相关联的”
“为了实现监视器的排他性监视能力,JVM 为每一个对象和类都关联一个锁”

这个也叫互斥锁,就说指多个线程同时来获取对象的锁,监视器负责监管对象的锁,一次只允许一个线程拿到锁。采用这样的方法,使得多个线程可以同步顺序执行。

Synchronized 关键字的四种用法:

public synchronized void someMethod() {

// 方法体

}

该方法被声明为同步方法,也就是说执行该方法必须获得当前对象锁

public synchronized static void someMethod() {

// 方法体

}

该方法被声明为同步方法,由于方法为静态的,因此无法获得当前对象的锁,所以这个方法获得的是当前类的 class 对象锁,也就是说执行该方法必须先获得这个类的 class 对象锁

public static void someMethod() {

synchronized(SynTest.class){

// 方法体

}

}

该方法包含同步代码块,也就是说执行 syn 语句块的代码前必须先获得当前类的 class 对象锁。

public void someMethod() {

synchronized(this){

// 方法体

}

}

该方法包含同步代码块,也就是说执行 syn 语句块的代码前必须先获得当前对象锁

synchronized 用于指定同步代码块,并且它只能锁定对象,无法锁定基本数据类型。

线程的死锁

死锁,既永远也解不开的锁。在程序开发中我们应极力避免这种问题。

当线程 1 锁定了资源 A,同时线程 2 锁定了资源 B,这是线程 1 必须拿到资源 B 的锁,才能执行结束,从而释放资源 A。而线程 2 必须拿到资源 A,才能释放资源 B。

形成这样的僵局,两个线程就无法结束,造成死锁。

关于死锁的例子程序:

publicclass DeadLock {

publicstaticvoid main(String[] args) {

Thread t1 = new Thread(new DL(true));

Thread t2 = new Thread(new DL(false));

t1.start();  t2.start();

}

}

class DL implements Runnable {

static Object lockX = new Object();

static Object lockY = new Object();

booleanflag = true;

public DL(boolean flag) {

this.flag = flag;

}

@Override

publicvoid run() {

if(flag) {

synchronized(lockX) {

try {

Thread.sleep(2000);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized(lockY) {// 阻塞

System.out.println(执行结束

}

}

} else {

synchronized(lockY) {

try {

Thread.sleep(2000);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized(lockX) {

System.out.println(执行结束

}

}

}

}

}

到此,关于“怎么创建 Thread 类”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注丸趣 TV 网站,丸趣 TV 小编会继续努力为大家带来更多实用的文章!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-16发表,共计4415字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)