Java线程之多线程与多进程(3)——Java中的多线程

单线程

任何程序至少有一个线程,即使你没有主动地创建线程,程序从一开始执行就有一个默认的线程,被称为主线程,只有一个线程的程序称为单线程程序。如下面这一简单的代码,没有显示地创建一个线程,程序从main开始执行,main本身就是一个线程(主线程),单个线程从头执行到尾。

public class Test{public static void main(String args[]) {System.out.println("1-100:");for (int i = 0; i < 100; i ++) {System.out.println(i + 1);}}
}

创建线程

单线程程序简单明了,但有时无法满足特定的需求。如一个文字处理的程序,我在打印文章的同时也要能对文字进行编辑,如果是单线程的程序则要等打印机打印完成之后你才能对文字进行编辑,但打印的过程一般比较漫长,这是我们无法容忍的。如果采用多线程,打印的时候可以单独开一个线程去打印,主线程可以继续进行文字编辑。在程序需要同时执行多个任务时,可以采用多线程。

在程序需要同时执行多个任务时,可以采用多线程。Java给多线程编程提供了内置的支持,提供了两种创建线程方法:1.通过实现Runable接口;2.通过继承Thread类。

Thread是JDK实现的对线程支持的类,Thread类本身实现了Runnable接口,所以Runnable是显示创建线程必须实现的接口; Runnable只有一个run方法,所以不管通过哪种方式创建线程,都必须实现run方法。我们来看一个例子。

【Demo2】:线程的创建和使用

//通过实现Runnable方法 
class ThreadA implements Runnable {private Thread thread;private String threadName;public ThreadA(String threadName) {thread = new Thread(this, threadName);this.threadName = threadName;}//实现run方法public void run() {for (int i = 0; i < 10; i ++) {System.out.println(threadName + ": " + i);}}public void start() {thread.start();}
}//通过继承Thread类
class ThreadB extends Thread {private String threadName;public ThreadB(String threadName) {super(threadName);this.threadName = threadName;}//实现run方法public void run() {for (int i = 0; i < 10; i ++) {System.out.println(threadName + ": " + i);}}
}public class Test{public static void main(String args[]) {ThreadA threadA = new ThreadA("ThreadA");ThreadB threadB = new ThreadB("ThreadB");threadA.start();threadB.start();}
}

运行结果:

说明:上面的例子中例举了两种实现线程的方式。大部分情况下选择实现Runnable接口的方式会优于继承Thread的方式,因为: 

1. 从 Thread 类继承会强加类层次; 

2. 有些类不能继承Thread类,如要作为线程运行的类已经是某一个类的子类了,但Java只支持单继承,所以不能再继承Thread类了。

--------------------------------------------------------------------------------------------------------------------------------------------

线程同步

线程与线程之间的关系,有几种:

模型一:简单的线程,多个线程同时执行,但各个线程处理的任务毫不相干,没有数据和资源的共享,不会出现争抢资源的情况。这种情况下不管有多少个线程同时执行都是安全的,其执行模型如下: 

处理相互独立的任务 
图 1:处理相互独立的任务

型二:复杂的线程,多个线程共享相同的数据或资源,就会出现多个线程争抢一个资源的情况。这时就容易造成数据的非预期(错误)处理,是线程不安全的,其模型如下: 

多个线程共享相同的数据或资源 
图 2:多个线程共享相同的数据或资源

在出现模型二的情况时就要考虑线程的同步,确保线程的安全。Java中对线程同步的支持,最常见的方式是添加synchronized同步锁。

我们通过一个例子来看一下线程同步的应用。

买火车票是大家春节回家最为关注的事情,我们就简单模拟一下火车票的售票系统(为使程序简单,我们就抽出最简单的模型进行模拟):有500张从北京到上海的火车票,在8个窗口同时出售,保证系统的稳定性和数据的原子性。 

 

模拟火车票售票系统 

图 3:模拟火车票售票系统

【Demo3】:火车票售票系统模拟程序
/*** 模拟服务器的类*/
class Service {private String ticketName;    //票名private int totalCount;        //总票数private int remaining;        //剩余票数public Service(String ticketName, int totalCount) {this.ticketName = ticketName;this.totalCount = totalCount;this.remaining = totalCount;}public synchronized int saleTicket(int ticketNum) {if (remaining > 0) {remaining -= ticketNum;try {        //暂停0.1秒,模拟真实系统中复杂计算所用的时间Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}if (remaining >= 0) {return remaining;} else {remaining += ticketNum;return -1;}}return -1;}public synchronized int getRemaining() {return remaining;}public String getTicketName() {return this.ticketName;}}/*** 售票程序*/
class TicketSaler implements Runnable {private String name;private Service service;public TicketSaler(String windowName, Service service) {this.name = windowName;this.service = service;}@Overridepublic void run() {while (service.getRemaining() > 0) {synchronized (this){System.out.print(Thread.currentThread().getName() + "出售第" + service.getRemaining() + "张票,");int remaining = service.saleTicket(1);if (remaining >= 0) {System.out.println("出票成功!剩余" + remaining + "张票.");} else {System.out.println("出票失败!该票已售完。");}}}}
}

测试程序:

/*** 测试类*/
public class TicketingSystem {public static void main(String args[]) {Service service = new Service("北京-->上海", 500);TicketSaler ticketSaler = new TicketSaler("售票程序", service);//创建8个线程,以模拟8个窗口Thread threads[] = new Thread[8];for (int i = 0; i < threads.length; i++) {threads[i] = new Thread(ticketSaler, "窗口" + (i + 1));System.out.println("窗口" + (i + 1) + "开始出售 " + service.getTicketName() + " 的票...");threads[i].start();}}
}

结果如下:

窗口1开始出售 北京–>赣州 的票… 
窗口2开始出售 北京–>赣州 的票… 
窗口3开始出售 北京–>赣州 的票… 
窗口4开始出售 北京–>赣州 的票… 
窗口5开始出售 北京–>赣州 的票… 
窗口6开始出售 北京–>赣州 的票… 
窗口7开始出售 北京–>赣州 的票… 
窗口8开始出售 北京–>赣州 的票… 
窗口1出售第500张票,出票成功!剩余499张票. 
窗口1出售第499张票,出票成功!剩余498张票. 
窗口6出售第498张票,出票成功!剩余497张票. 
窗口6出售第497张票,出票成功!剩余496张票. 
窗口1出售第496张票,出票成功!剩余495张票. 
窗口1出售第495张票,出票成功!剩余494张票. 
窗口1出售第494张票,出票成功!剩余493张票. 
窗口2出售第493张票,出票成功!剩余492张票. 
窗口2出售第492张票,出票成功!剩余491张票. 
窗口2出售第491张票,出票成功!剩余490张票. 
窗口2出售第490张票,出票成功!剩余489张票. 
窗口2出售第489张票,出票成功!剩余488张票. 
窗口2出售第488张票,出票成功!剩余487张票. 
窗口6出售第487张票,出票成功!剩余486张票. 
窗口6出售第486张票,出票成功!剩余485张票. 
窗口3出售第485张票,出票成功!剩余484张票. 
……

在上面的例子中,涉及到数据的更改的Service类saleTicket方法和TicketSaler类run方法都用了synchronized同步锁进行同步处理,以保证数据的准确性和原子性。

关于synchronized更详细的用法请参见:《Java中Synchronized的用法》


线程控制

在多线程程序中,除了最重要的线程同步外,还有其它的线程控制,如线程的中断、合并、优先级等。

线程等待(wait、notify、notifyAll)

Wait:使当前的线程处于等待状态; 
Notify:唤醒其中一个等待线程; 
notifyAll:唤醒所有等待线程。

详细用法参见:《 Java多线程中wait, notify and notifyAll的使用》


线程中断(interrupt)

在Java提供的线程支持类Thread中,有三个用于线程中断的方法: 
1、public void interrupt(); 中断线程。 
2、public static boolean interrupted(); 是一个静态方法,用于测试当前线程是否已经中断,并将线程的中断状态 清除。所以如果线程已经中断,调用两次interrupted,第二次时会返回false,因为第一次返回true后会清除中断状态。 
3、public boolean isInterrupted(); 测试线程是否已经中断。

【Demo4】:线程中断的应用

/*** 打印线程*/
class Printer implements Runnable {public void run() {while (!Thread.currentThread().isInterrupted()) {     //如果当前线程未被中断,则执行打印工作System.out.println(Thread.currentThread().getName() + "打印中… …");}if (Thread.currentThread().isInterrupted()) {System.out.println("interrupted:" +  Thread.interrupted());       //返回当前线程的状态,并清除状态System.out.println("isInterrupted:" +  Thread.currentThread().isInterrupted());}}
}

调用代码:

Printer printer = new Printer();
Thread printerThread = new Thread(printer, "打印线程");
printerThread.start();
try {Thread.sleep(100);
} catch (InterruptedException e) {e.printStackTrace();
}
System.out.println("有紧急任务出现,需中断打印线程.");
System.out.println("中断前的状态:" + printerThread.isInterrupted());
printerThread.interrupt();       // 中断打印线程
System.out.println("中断前的状态:" + printerThread.isInterrupted());

结果:

打印线程打印中… … 
… … 
打印线程打印中… … 
有紧急任务出现,需中断打印线程. 
打印线程打印中… … 
中断前的状态:false 
打印线程打印中… … 
中断前的状态:true 
interrupted:true 
isInterrupted:false

线程合并(join)

所谓合并,就是等待其它线程执行完,再执行当前线程,执行起来的效果就好像把其它线程合并到当前线程执行一样。其执行关系如下: 

线程合并的过程 
图 4:线程合并的过程

public final void join() 
等待该线程终止

public final void join(long millis); 
等待该线程终止的时间最长为 millis 毫秒。超时为 0 意味着要一直等下去。

public final void join(long millis, int nanos) 
等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒

这个常见的一个应用就是安装程序,很多大的软件都会包含多个插件,如果选择完整安装,则要等所有的插件都安装完成才能结束,且插件与插件之间还可能会有依赖关系。

【Demo5】:线程合并

/*** 插件1*/
class Plugin1 implements Runnable {@Overridepublic void run() {System.out.println("插件1开始安装.");System.out.println("安装中...");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("插件1完成安装.");}
}/*** 插件2*/
class Plugin2 implements Runnable {@Overridepublic void run() {System.out.println("插件2开始安装.");System.out.println("安装中...");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("插件2完成安装.");}
}

合并线程的调用:

System.out.println("主线程开启...");
Thread thread1 = new Thread(new Plugin1());
Thread thread2 = new Thread(new Plugin2());
try {thread1.start();   //开始插件1的安装thread1.join();       //等插件1的安装线程结束thread2.start();   //再开始插件2的安装thread2.join();       //等插件2的安装线程结束,才能回到主线程
} catch (InterruptedException e) {e.printStackTrace();
}
System.out.println("主线程结束,程序安装完成!");

结果如下:

主线程开启… 
插件1开始安装. 
安装中… 
插件1完成安装. 
插件2开始安装. 
安装中… 
插件2完成安装. 
主线程结束,程序安装完成!

优先级(Priority)

线程优先级是指获得CPU资源的优先程序。优先级高的容易获得CPU资源,优先级底的较难获得CPU资源,表现出来的情况就是优先级越高执行的时间越多。

Java中通过getPriority和setPriority方法获取和设置线程的优先级。Thread类提供了三个表示优先级的常量:MIN_PRIORITY优先级最低,为1;NORM_PRIORITY是正常的优先级;为5,MAX_PRIORITY优先级最高,为10。我们创建线程对象后,如果不显示的设置优先级的话,默认为5。

【Demo】:线程优先级

/*** 优先级*/
class PriorityThread implements Runnable{@Overridepublic void run() {for (int i = 0; i < 1000; i ++) {System.out.println(Thread.currentThread().getName() + ": " + i);}}
}

调用代码:

//创建三个线程
Thread thread1 = new Thread(new PriorityThread(), "Thread1");
Thread thread2 = new Thread(new PriorityThread(), "Thread2");
Thread thread3 = new Thread(new PriorityThread(), "Thread3");
//设置优先级
thread1.setPriority(Thread.MAX_PRIORITY);
thread2.setPriority(8);
//开始执行线程
thread3.start();
thread2.start();
thread1.start();

从结果中我们可以看到线程thread1明显比线程thread3执行的快。


---------------------------------结束--------->仅供学习

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/274019.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java 线程多线程编程3---线程同步之生产者与消费者问题

生产者与消费者问题&#xff1a; 第一步&#xff1a;把架子搭起来 package com.zhj.www;public class ProceduerConsumer {public static void main(String[] args) {} }//馒头实体 class wotou{int id;wotou(int id) {this.id id;}public String toString() {return "wo…

windows 服务实例

参考来源:http://blog.csdn.net/morewindows/article/details/6858216 参考来源: http://hi.baidu.com/tfantasy/item/aefa43d66b470a2b38f6f76c 剩下的都是我自己整理的。 在VS2012中新建一个Windows 服务的项目。然后在解决方案目录下找到Services1.cs&#xff0c;切换到代码…

Java 线程多线程编程2---线程同步

来模拟一个死锁&#xff08;互相等待&#xff09;&#xff1a; TestDeadLock.java package com.zhj.www;public class TestDeadLock implements Runnable {public int flag 1;static Object o1 new Object();static Object o2 new Object();public void run() {System.out.p…

Java网络编程1---基础

TCP/IP:事实上的标准 自己编的应用程序&#xff1a;应用层 TCP/UDP层 IP层 物理层 数据封装&#xff1a;第五层只与第四层打交道。 数据拆封《TCP/IP详解》网络底层 IP巨大的贡献&#xff1a;提供了独一无二的IP地址。 内网IP&#xff1a;虚假的 子网掩码&#xff1a;255.255.2…

Java网络编程2---Socket-TCP编程

Sockct:插座Socket是关于TCP的。 端口号&#xff1a;两个字节->65536个端口号&#xff0c;一个应用程序占多个端口号&#xff1b; 但是假设一个应用程序占一个端口号&#xff1b;一台电脑会有65535个应用程序。 自己编写程序要占用端口号1024以上后的。 80端口&#xff1a;网…

winform绑定多张图片

开发winform程序的时候经常设计到要显示多张图片的问题&#xff0c;其解决思路一般是先遍历文件夹中的所有图片&#xff0c;然后再把这些图片添加到ImageList控件中&#xff0c;最后再绑定显示出来。这里我们介绍两种绑定的方法&#xff1a; &#xff08;一&#xff09;动态生成…

Java网络编程3---Socket-UDP编程

栗子&#xff1a;TestUDPServer.java 服务器端&#xff1a; package com.zhj.www;import java.net.DatagramPacket; import java.net.DatagramSocket;public class TestUDPServer {public static void main(String[] args)throws Exception {byte buf[] new byte[1024];Datagr…

Java GUI 基础知识

这部分主要包含AWT、组件和容器、布局管理器Component&#xff1a;所有可以和用户交互的图形元素&#xff0c;他的子类有&#xff1a;输入框… Java.awt及其子包 Container&#xff1a;容器&#xff0c;容纳其他各种各样的Component的元素。 Panel&#xff1a;可以容纳其他元素…

Java GUI 基础知识2 监听机制

TestActionEvent.java没有调用方法&#xff0c;但是有反应。反应自己要编写程序有反应。 事件模型&#xff1a;一定要有某些反应。 写程序&#xff0c;监听的操作是自动发生的&#xff0c;一直监听。钩子函数&#xff0c;&#xff08;回调函数&#xff09; 怎么让它自动执行&am…

数据结构 二、向量(接口与实现and可扩容向量)

ADT操作实例&#xff1a;Disordered&#xff1a;显示出3对逆序紧邻对。Vector模板类初始有效空间为0&#xff1b;基于复制的构造描述区间&#xff1a;左闭右开 为什么*2&#xff1f;有限时间内不必要为扩容而打断。 2、可扩充向量左移一位&#xff1a;加一倍

Android 服务(Service)

一、服务的解释 服务&#xff08;Service&#xff09;是Android中实现后台运行的解决方案&#xff0c;它适合那些去执行不需要和用户交互而且还要求长期运行的任务。服务的运行不依赖任何的与任何用户界面&#xff0c;即使程序被切换到后台&#xff0c;或者用户打开了另外一个应…

Moravec角点检测算子

Moravec角点检测算子 Moravec 在1981年提出Moravec角点检测算子[1]&#xff0c;并将它应用于立体匹配。 首先, 计算每个像素点的兴趣值, 即以该像素点为中心, 取一个w*w(如:5x5)的方形窗口, 计算0度、45度、90度、135度四个方向灰度差的平方和, 取其中的最小值作为该像素点的兴…

文件夹生成工具

很简单的一个小工具,输入一个字符串,可以为你生成相应的文件夹. 至于有什么用?我公司一个策划拿一顿饭给我要的. 下载地址: http://pan.baidu.com/s/1d0ewl 转载于:https://www.cnblogs.com/WhyEngine/p/3350053.html

(转载)深入理解Linux中内存管理---分段与分页简介

首先&#xff0c;必须要阐述一下这篇文章的主题是Linux内存管理中的分段和分页技术。 来回顾一下历史&#xff0c;在早期的计算机中&#xff0c;程序是直接运行在物理内存上的。换句话说&#xff0c;就是程序在运行的过程中访问的都是物理地址。如果这个系统只运行一个程序&…

java中main函数的args参数

先说一下args的作用&#xff1a;我们习惯将一些有用的参数传递给我们定义的函数&#xff0c;那么可曾想过有参数传递给main函数&#xff1f;args就是传递给main函数的一个数组参数。可是main函数作为程序(application程序)的入口点&#xff0c;是由系统自动调用的&#xff0c;怎…

黑马程序员-------------(十)Java基础知识加强(一)

JDK1.5新特性 目录1.之前已经学习过的JDK1.5新特性2.静态导入 StaticImport3.可变参数 ...4.高级for循环5.枚举6.泛型 Generic7.注解注&#xff1a;本章全部为重点内容。###################################################################################################…

java例子:九九乘法表

来吧直接上代码&#xff1a;public class Test{public static void main(String[] args) {for(int i 1; i<9;i){for (int j 1; j < i ;j ) {System.out.print(j"x"i""j*i" ");}System.out.print("\n");}} }运行之后&#xff1…

算法:查找

查找算法&#xff08;比较&#xff09;基本思想顺序查找顺序查找也称为线形查找&#xff0c;属于无序查找算法。从数据结构线形表的一端开始&#xff0c;顺序扫描&#xff0c;依次将扫描到的结点关键字与给定值k相比较&#xff0c;若相等则表示查找成功&#xff1b;若扫描结束仍…

query上传插件uploadify参数详细分析

query上传插件uploadify参数详细分析 Uploadify Version 3.2 官网&#xff1a;http://www.uploadify.com/ 注&#xff1a;文件包里有两个js分别是&#xff1a;jquery.uploadify.js 和 jquery.uploadify.min.js&#xff0c;两者其实一样&#xff0c;只需载入其中一个js即可。很明…

type_traits应用

工作遇到这样的例子&#xff0c;对不同数据进行计算处理&#xff0c;得到一组结果集&#xff0c;包括计数、比例等。各个计算处理过程不同&#xff0c;结果集数据结构相同&#xff0c;但并非所有计算过程均有计数、比例的结果&#xff0c;有些可能只有计数&#xff0c;有些只有…