Java多线程编程,python在线编程平台

http://www.itjxue.com  2023-01-21 17:46  来源:未知  点击次数: 

什么是Java多线程编程?

一、 什么是多线程:

我们现在所使用操作系统都是多任务操作系统(早期使用的DOS操作系统为单任务操作系统),多任务操作指在同一时刻可以同时做多件事(可以同时执行多个程序)。

多进程:每个程序都是一个进程,在操作系统中可以同时执行多个程序,多进程的目的是为了有效的使用CPU资源,每开一个进程系统要为该进程分配相关的系统资源(内存资源)

多线程:线程是进程内部比进程更小的执行单元(执行流|程序片段),每个线程完成一个任务,每个进程内部包含了多个线程每个线程做自己的事情,在进程中的所有线程共享该进程的资源;

主线程:在进程中至少存在一个主线程,其他子线程都由主线程开启,主线程不一定在其他线程结束后结束,有可能在其他线程结束前结束。Java中的主线程是main线程,是Java的main函数;

二、 Java中实现多线程的方式:

继承Thread类来实现多线程:

当我们自定义的类继承Thread类后,该类就为一个线程类,该类为一个独立的执行单元,线程代码必须编写在run()方法中,run方法是由Thread类定义,我们自己写的线程类必须重写run方法。

run方法中定义的代码为线程代码,但run方法不能直接调用,如果直接调用并没有开启新的线程而是将run方法交给调用的线程执行

要开启新的线程需要调用Thread类的start()方法,该方法自动开启一个新的线程并自动执行run方法中的内容

请点击输入图片描述

结果:

请点击输入图片描述

java多线程的启动顺序不一定是线程执行的顺序,各个线程之间是抢占CPU资源执行的,所有有可能出现与启动顺序不一致的情况。

CPU的调用策略:

如何使用CPU资源是由操作系统来决定的,但操作系统只能决定CPU的使用策略不能控制实际获得CPU执行权的程序。

线程执行有两种方式:

1.抢占式:

目前PC机中使用最多的一种方式,线程抢占CPU的执行权,当一个线程抢到CPU的资源后并不是一直执行到此线程执行结束,而是执行一个时间片后让出CPU资源,此时同其他线程再次抢占CPU资源获得执行权。

2.轮循式;

每个线程执行固定的时间片后让出CPU资源,以此循环执行每个线程执行相同的时间片后让出CPU资源交给下一个线程执行。

希望对您有所帮助!~

java中学习线程应该怎么去学习?

打个比方,用户注册,如果用户注册有如下步骤

1:给用户在数据库中添加用户信息

2:给用户的手机号发送欢迎短信

3:系统中其它模块和用户关联,需要同时添加其它的数据

这个时候,如果使用单线程,用户提交注册信息之后,系统拿到注册信息,就要按照步骤1,2,3这么一步一步走下去,在这过程中,用户只能等待,等到系统把这些步骤走完了,用户才能得到响应,才能登陆系统,这个过程会很长,用户体验不好

但是如果使用多线程,在拿到用户注册信息之后,主进程把这些任务分给多个线程去做,每个线程做一件事,效率是不是提高了,时间是不是缩短了,并且,主线程可以把关键信息录入系统之后就直接响应用户,其它事情可以让线程在后台慢慢执行,这样用户体验就会好很多。

如何用Java编写多线程

在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口。

对于直接继承Thread的类来说,代码大致框架是:

?

123456789101112 class 类名 extends Thread{ 方法1; 方法2; … public void run(){ // other code… } 属性1; 属性2; … }

先看一个简单的例子:

?

12345678910111213141516171819202122232425262728 /** * @author Rollen-Holt 继承Thread类,直接调用run方法 * */class hello extends Thread { public hello() { } public hello(String name) { this.name = name; } public void run() { for (int i = 0; i 5; i++) { System.out.println(name + "运行 " + i); } } public static void main(String[] args) { hello h1=new hello("A"); hello h2=new hello("B"); h1.run(); h2.run(); } private String name; }

【运行结果】:

A运行 0

A运行 1

A运行 2

A运行 3

A运行 4

B运行 0

B运行 1

B运行 2

B运行 3

B运行 4

我们会发现这些都是顺序执行的,说明我们的调用方法不对,应该调用的是start()方法。

当我们把上面的主函数修改为如下所示的时候:

?

123456 public static void main(String[] args) { hello h1=new hello("A"); hello h2=new hello("B"); h1.start(); h2.start(); }

然后运行程序,输出的可能的结果如下:

A运行 0

B运行 0

B运行 1

B运行 2

B运行 3

B运行 4

A运行 1

A运行 2

A运行 3

A运行 4

因为需要用到CPU的资源,所以每次的运行结果基本是都不一样的,呵呵。

注意:虽然我们在这里调用的是start()方法,但是实际上调用的还是run()方法的主体。

那么:为什么我们不能直接调用run()方法呢?

我的理解是:线程的运行需要本地操作系统的支持。

如果你查看start的源代码的时候,会发现:

?

1234567891011121314151617 public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0 || this != me) throw new IllegalThreadStateException(); group.add(this); start0(); if (stopBeforeStart) { stop0(throwableFromStop); } } private native void start0();

注意我用红色加粗的那一条语句,说明此处调用的是start0()。并且这个这个方法用了native关键字,次关键字表示调用本地操作系统的函数。因为多线程的实现需要本地操作系统的支持。

但是start方法重复调用的话,会出现java.lang.IllegalThreadStateException异常。

通过实现Runnable接口:

大致框架是:

?

123456789101112 class 类名 implements Runnable{ 方法1; 方法2; … public void run(){ // other code… } 属性1; 属性2; … }

来先看一个小例子吧:

?

123456789101112131415161718192021222324252627282930 /** * @author Rollen-Holt 实现Runnable接口 * */class hello implements Runnable { public hello() { } public hello(String name) { this.name = name; } public void run() { for (int i = 0; i 5; i++) { System.out.println(name + "运行 " + i); } } public static void main(String[] args) { hello h1=new hello("线程A"); Thread demo= new Thread(h1); hello h2=new hello("线程B"); Thread demo1=new Thread(h2); demo.start(); demo1.start(); } private String name; }

【可能的运行结果】:

线程A运行 0

线程B运行 0

线程B运行 1

线程B运行 2

线程B运行 3

线程B运行 4

线程A运行 1

线程A运行 2

线程A运行 3

线程A运行 4

关于选择继承Thread还是实现Runnable接口?

其实Thread也是实现Runnable接口的:

?

12345678 class Thread implements Runnable { //… public void run() { if (target != null) { target.run(); } } }

其实Thread中的run方法调用的是Runnable接口的run方法。不知道大家发现没有,Thread和Runnable都实现了run方法,这种操作模式其实就是代理模式。关于代理模式,我曾经写过一个小例子呵呵,大家有兴趣的话可以看一下:

Thread和Runnable的区别:

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

?

1234567891011121314151617181920212223 /** * @author Rollen-Holt 继承Thread类,不能资源共享 * */class hello extends Thread { public void run() { for (int i = 0; i 7; i++) { if (count 0) { System.out.println("count= " + count--); } } } public static void main(String[] args) { hello h1 = new hello(); hello h2 = new hello(); hello h3 = new hello(); h1.start(); h2.start(); h3.start(); } private int count = 5; }

【运行结果】:

count= 5

count= 4

count= 3

count= 2

count= 1

count= 5

count= 4

count= 3

count= 2

count= 1

count= 5

count= 4

count= 3

count= 2

count= 1

大家可以想象,如果这个是一个买票系统的话,如果count表示的是车票的数量的话,说明并没有实现资源的共享。

我们换为Runnable接口:

?

12345678910111213141516171819 /** * @author Rollen-Holt 继承Thread类,不能资源共享 * */class hello implements Runnable { public void run() { for (int i = 0; i 7; i++) { if (count 0) { System.out.println("count= " + count--); } } } public static void main(String[] args) { hello he=new hello(); new Thread(he).start(); } private int count = 5; }

【运行结果】:

count= 5

count= 4

count= 3

count= 2

count= 1

总结一下吧:

实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。

所以,本人建议大家劲量实现接口。

?

北大青鸟设计培训:java多线程编程中涉及的基础知识点?

线程设计在软件开发领域中是非常常见的一个设计构成,今天厦门北大青鸟就一起来了解一下,java多线程编程中都涉及到了哪些基础知识点。

顺序用于表示多个操作“依次处理”。

比如把十个操作交给一个人来处理时,这个人要一个一个地按顺序来处理并行用于标识多个操作“同时处理”。

比如十个操作分给两个人处理时,这两个人就会并行来处理。

并发相对于顺序和并行来说比较抽象,用于表示“将一个操作分割成多个部分并且允许无序处理”。

比如将十个操作分成相对独立的两类,这样便能够开始并发处理了。

如果一个人来处理,这个人就是顺序处理分开的并发操作,而如果是两个人,这两个人就可以并行处理同一个操作。

总结多线程程序都是并发处理的。

如果CPU只有一个,那么并发处理就是顺序执行的,而如果有多个CPU,那么并发处理就可能会并行运行。

等待队列所有实例都拥有一个等待队列,它是在实例的wait方法执行后停止操作的线程队列。

就好比为每个实例准备的线程休息室在执行wait方法后,线程便会暂停操作,进入等待队列这个休息室。

除非发生下列某一情况,否则线程会一直在等待队列中休眠。

有其他线程的notify方法来唤醒线程有其他线程的notifyAll方法来唤醒线程有其他线程的interrupt方法来唤醒线程wait方法超时notify方法该方法会将等待队列中的一个线程去除。

同wait方法一样,若要执行notify方法,线程也必须持有要调用的实例的锁。

notifyAll方法notify方法仅唤醒一个线程,而notifyAll则唤醒所有线程,这是两者之间的区别同wait方法和notify方法一样,notifyAll方法也只能由持有要调用的实例锁的线程调用notify和notifyAll选择notify方法和notifyAll方法非常相似,到底该使用哪个?实际上,这很难选择,由于notify唤醒的线程较少,所以处理速度要比使用notifyAll时快。

但使用notify时,如果处理不好,程序便可能会停止。

一般来说,使用notifyAll时的代码要比使用notify时的更为健壮。

多线程的java 程序如何编写?

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

新建状态:

使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

就绪状态:

当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

运行状态:

如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

阻塞状态:

如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。

其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

死亡状态:

一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

(责任编辑:IT教学网)

更多

推荐Discuz!建站文章