Java多线程
一:进程与线程
概述:几乎任何的操作系统都支持运行多个任务,通常一个任务就是一个程序,而一个程序就是一个进程。当一个进程运行时,内部可能包括多个顺序执行流,每个顺序执行流就是一个线程。
进程:进程是指处于运行过程中的程序,并且具有一定的独立功能。进程是系统进行资源分配和调度的一个单位。当程序进入内存运行时,即为进程。
进程的三个特点:
1:独立性:进程是系统中独立存在的实体,它可以独立拥有资源,每一个进程都有自己独立的地址空间,没有进程本身的运行,用户进程不可以直接访问其他进程的地址空间。
2:动态性:进程和程序的区别在于进程是动态的,进程中有时间的概念,进程具有自己的生命周期和各种不同的状态。
3:并发性:多个进程可以在单个处理器上并发执行,互不影响。
并发性和并行性是不同的概念:并行是指同一时刻,多个命令在多个处理器上同时执行;并发是指在同一时刻,只有一条命令是在处理器上执行的,但多个进程命令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果
线程:线程是进程的组成部分,一个进程可以拥有多个线程,而一个线程必须拥有一个父进程。线程可以拥有自己的堆栈,自己的程序计数器和自己的局部变量,但不能拥有系统资源。它与父进程的其他线程共享该进程的所有资源。
线程的特点:
线程可以完成一定任务,可以和其它线程共享父进程的共享变量和部分环境,相互协作来完成任务。
线程是独立运行的,其不知道进程中是否还有其他线程存在。
线程的执行是抢占式的,也就是说,当前执行的线程随时可能被挂起,以便运行另一个线程。
一个线程可以创建或撤销另一个线程,一个进程中的多个线程可以并发执行。
二:线程的创建及使用
java使用Thread类代表线程,所有的线程对象都必须是Thread或者其子类的实例,每个线程的作用是完成一定任务,实际上是就是执行一段程序流(一段顺序执行的代码)
方案一:继承Thread类创建线程类
步骤:1.定义Thread类的子类 并重写该类的Run方法,该run方法的方法体就代表了该线程需要完成的任务
2.创建Thread类的实例,即创建了线程对象
3.调用线程的start方法来启动线程
package Thread01; public class MyThread extends Thread {
private int i; @Override
public void run() {
for (; i < 10; i++) {
System.out.println(getName()+"\t"+i);
}
}
}
测试方法
package Thread01; public class MyTest {
public static void main(String[] args) {
for (int i = 0; i <10; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
if(i==5){
MyThread mt=new MyThread();
MyThread mt2=new MyThread();
mt.start();
mt2.start();
}
}
}
}
方案二:实现Runnable接口
1:定义Runnable接口的实现类,并重写它的Run方法,run方法同样是该线程的执行体!
2:创建Runnable实现类的实例,并将此实例作为Thread的target创建一个Thread对象,该Thread对象才是真正的线程对象!
3:调用start方法启动该线程
package Thread02; public class SecondThread implements Runnable{
private int i;
public void run() {
for (; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==20)
{
System.out.println(Thread.currentThread().getName()+"执行完毕");
}
}
}
}
测试方法
package Thread02; public class MyTest {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==5)
{
SecondThread s1=new SecondThread();
Thread t1=new Thread(s1,"线程1");
Thread t2=new Thread(s1,"线程2");
t1.start();
t2.start(); }
}
}
}
结论:采用Ruunable接口的方式创建多个线程可以共享线程类的实例变量,这是因为在这种方式下,程序创建的Runnable对象只是线程的target,而多个线程可以共享一个target,所以多个线程可以共享一个实例变量
通过Runnable实现多线程其实就是将run包装成线程的执行体,但是目前java无法将任意方法包装成线程执行体
方案三:使用callable和future创建线程
从Java5开始,Java提供 Callable接口,Callable接口提供了一个call()方法可以作为线程执行体,看起来和Runnable很像,但call()方法更强大——call()方法可以有返回值、call()方法可以抛出异常
Java5提供了Future接口来代表Callable接口的call()方法的返回值,并为Future接口提供了一个FutureTask实现类,该实现类实现类Future接口,也实现了Runnable接口——可以作为Thread的target。
实现步骤:
1:创建Callable接口的实现类,并实现call方法,该call方法会成为线程执行体,且call方法具有返回值,在创建callable接口的实现类!
2:使用FutrueTask类来包装Callable对象,该FutrueTask封装类Callable的call方法的返回值
3:使用FutrueTask对象作为Thread的target创建并启动新线程!
4:使用FutrueTask的get方法获取执行结束后的返回值
package Thread03; import java.util.concurrent.Callable; public class Target implements Callable<Integer> {
int i=0;
public Integer call() throws Exception {
for (; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+""+i);
}
return i;
} }
测试方法
package Thread03; import java.util.concurrent.FutureTask; public class ThirdThread {
public static void main(String[] args) {
Target t1=new Target();
FutureTask<Integer> ft=new FutureTask<Integer>(t1);
Thread t2=new Thread(ft,"新线程");
t2.start();
try {
System.out.println(ft.get());
} catch (Exception e) {
}
}
}
结论:采取Runnable、Callable的优势在于——线程类只是实现了Runnable或Callable接口,还可以继承其它类;在这种方法下,多个线程可以共享一个target对象,因此非常适合多个相同线程处理同一份资源的情况,从而将CPU、代码和数据分开,形参清晰的模型,体现了面对对象的编程思想。劣势在于编程复杂度略高。
三:线程的状态
当线程被创建并被启动时,它既不是一启动就进入了执行状态,在线程的生命周期中,它要经过new(新建),就绪(Runnable),运行(Running),阻塞(Blocked),dead(死亡)。
当线程启动之后,它不可能一直霸占着cpu独自运行,所有cpu需要在多条线程轮流切换,于是线程就也会多次在运行.就绪之间切换
1:新建和就绪状态
--新建状态:
当程序使用new关键字创建了一个线程时,该线程就处于新建状态。
此时的它和其它java对象一样,仅有虚拟机分配内存,并初始化成员变量的值。此时的线程对象并没有表现出线程的任何动态特征,程序也不会执行线程的线程执行体
--就绪状态:
当线程对象调用了start()方法后,该线程就处于就绪状态,java虚拟机会为其创建方法调用栈和程序计数器,处于该状态的线程并没有开始执行,只是表明该线程可以运行了,至于该线程何时运行,取决于JVM的调度。
注意!!!
启动线程要调用start方法,而不是run方法,永远不要调用线程的run方法,如果调用run方法,系统会把线程对象当作普通的对象,会吧线程的执行体当作普通方法来调用!在调用了run方法之后,该线程就不在处于新建状态,不要再调用该线程的start方法!java中只能对处于新建状态的线程使用start方法,否则将会引发IllegalThreadStateException异常!
2:运行状态和阻塞状态
当发生如下的几种情况时,将会进入阻塞状态:
当线程调用sleep方法主动放弃所占用的处理器资源
线程调用了一个阻塞时的IO方法,在该方法返回之前,线程会被阻塞
线程试图获得一个同步监视器,但该同步监视器正被其他线程锁持有
线程正在等待某个通知(notify)
程序调用了线程的suspend方法将该线程挂起
当以上几个情况,当发生如下的情况将会重新进入就绪状态
调用sleep()方法过了指定时间
线程调用的阻塞时IO方法依旧返回
线程成功地获得了试图获得的同步监视器
现在正在等待某个通知,而其它线程发出一个通知
处于挂起状态的线程被调用了resume()方法
注意!!!
线程从阻塞状态只能进入就绪状态,无法直接进入运行状态。就绪和运行状态之间的转换通常不受程序控制,而是系统线程的调度决定的。
调用yield()方法可以让处于运行时的线程转入就绪状态。
3:线程死亡
线程会以以下三种方式结束,结束后处于死亡状态
run或call方法执行完成,程序结束
线程抛出一个未捕获的Exception或者Error
直接调用该线程的stop方法来结束线程
当主线程结束时,其它线程不受任何影响,并不会随之结束。一旦子线程启动起来后,他就会拥有和主线程相同的地位,它不会受主线程影响。
为了测试某个线程是否死亡,可以调用该线程的isAlive方法,当线程处于就绪,运行,阻塞三种状态时,将返回true;当线程处于新建,死亡两种状态时返回为false。
不要试图对一个已经死亡的线程调用start方法让它重新启动,死亡后的线程无法作为线程使用。
如果处于非新建状态的线程使用start方法,就会引发IllegalThreadStateException异常。
Java多线程的更多相关文章
- 40个Java多线程问题总结
前言 Java多线程分类中写了21篇多线程的文章,21篇文章的内容很多,个人认为,学习,内容越多.越杂的知识,越需要进行深刻的总结,这样才能记忆深刻,将知识变成自己的.这篇文章主要是对多线程的问题进行 ...
- Java多线程基础知识篇
这篇是Java多线程基本用法的一个总结. 本篇文章会从一下几个方面来说明Java多线程的基本用法: 如何使用多线程 如何得到多线程的一些信息 如何停止线程 如何暂停线程 线程的一些其他用法 所有的代码 ...
- Java多线程系列--“JUC锁”03之 公平锁(一)
概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...
- Java多线程系列--“JUC锁”04之 公平锁(二)
概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ...
- Java多线程--让主线程等待子线程执行完毕
使用Java多线程编程时经常遇到主线程需要等待子线程执行完成以后才能继续执行,那么接下来介绍一种简单的方式使主线程等待. java.util.concurrent.CountDownLatch 使用c ...
- Java多线程 2 线程的生命周期和状态控制
一.线程的生命周期 线程状态转换图: 1.新建状态 用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态.处于新生状态的线程有自己的内存空间,通过调用start方法进入就 ...
- java 多线程 1 线程 进程
Java多线程(一).多线程的基本概念和使用 2012-09-10 16:06 5108人阅读 评论(0) 收藏 举报 分类: javaSE综合知识点(14) 版权声明:本文为博主原创文章,未经博 ...
- 一起阅读《Java多线程编程核心技术》
目录 第一章 Java多线程技能 (待续...)
- 第一章 Java多线程技能
1.初步了解"进程"."线程"."多线程" 说到多线程,大多都会联系到"进程"和"线程".那么这两者 ...
- java从基础知识(十)java多线程(下)
首先介绍可见性.原子性.有序性.重排序这几个概念 原子性:即一个操作或多个操作要么全部执行并且执行的过程不会被任何因素打断,要么都不执行. 可见性:一个线程对共享变量值的修改,能够及时地被其它线程看到 ...
随机推荐
- 用dubbo时遇到的一个序列化的坑
首先,这是标题党,问题并不是出现在序列化上,这是报错的一部分: Caused by: com.alibaba.dubbo.remoting.RemotingException: Failed to s ...
- 从Membership 到 .NET4.5 之 ASP.NET Identity
我们前面已经讨论过了如何在一个网站中集成最基本的Membership功能,然后深入学习了Membership的架构设计.正所谓从实践从来,到实践从去,在我们把Membership的结构吃透之后,我们要 ...
- Unity游戏内版本更新
最近研究了一下游戏内apk包更新的方法. ios对于应用的管理比较严格,除非热更新脚本,不太可能做到端内大版本包的更新.然而安卓端则没有此限制.因此可以做到不跳到网页或应用商店,就覆盖更新apk包. ...
- Swift enum(枚举)使用范例
//: Playground - noun: a place where people can play import UIKit var str = "Hello, playground& ...
- C++随笔:.NET CoreCLR之GC探索(4)
今天继续来 带大家讲解CoreCLR之GC,首先我们继续看这个GCSample,这篇文章是上一篇文章的继续,如果有不清楚的,还请翻到我写的上一篇随笔.下面我们继续: // Initialize fre ...
- ASP.NET MVC5----常见的数据注解和验证
只要一直走,慢点又何妨. 在使用MVC模式进行开发时,数据注解是经常使用的(模型之上操作),下面是我看书整理的一些常见的用法. 什么是验证,数据注解 验证 从全局来看,发现逻辑仅是整个验证的很小的一部 ...
- Python 基础之一变量和赋值
变量:程序在运行的时候会用到很多临时存储数据,这个时候就用到了变量,临时数据的名字. Python中变量不需要声明,直接可以使用,变量的数据类型由赋值确定. >>> name=&qu ...
- document.compatMode
在我电脑屏幕上显示的 电脑是 1920*1080这是在document.compatMode:css1Compat模式 window.screen.availWidth 1920 window.scr ...
- Atitit.attilax软件研发与项目管理之道
Atitit.attilax软件研发与项目管理之道 1. 前言4 2. 鸣谢4 3. Genesis 创世记4 4. 软件发展史4 5. 箴言4 6. 使徒行传 4 7. attilax书 4 8. ...
- VS2015 Git 源码管理工具简单入门
1.VS Git插件 1.1 环境 VS2015+GitLab 1.2 Git操作过程图解 1.3 常见名词解释 拉取(Pull):将远程版本库合并到本地版本库,相当于(Fetch+Meger) 获取 ...