线程的创建方式有两种:
implements Runnable和extends Thread。
继承Thread类:
package com.test.threadtest;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mThread thread = new mThread();
thread.start();
}
class mThread extends Thread{
@Override
public void run() {
}
}
}
这里当调用start()方法的时候就会,线程就会进入线程队列中,一旦线程获取到了CPU的时间片,线程就会执行run()方法。当run()方法执行完毕,线程就会被销毁。
实现Runnable接口:
package com.test.threadtest;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mThread mthread = new mThread();
Thread thread = new Thread(mthread);
thread.start();
}
class mThread implements Runnable{
@Override
public void run() {
}
}
}
两种方式的比较:
(1)Runnable方式可以避免Thread方式由于Java单继承特性带来的缺陷。
(2)Runnable的代码可以被多个线程(Thread实例)共享,适合于多个线程处理同一资源的情况。
第二不同之处的解释:
用实现Runnable接口的方式:
package com.test.threadtest;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mThread mthread = new mThread();
Thread thread1 = new Thread(mthread,"窗口一");
Thread thread2 = new Thread(mthread,"窗口二");
Thread thread3 = new Thread(mthread,"窗口三");
thread1.start();
thread2.start();
thread3.start();
}
class mThread implements Runnable{
private int ticketsCount = 5;
@Override
public void run() {
while(ticketsCount>0){
ticketsCount--;
System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余票数为:"+ticketsCount);
}
}
}
}
结果截图:
用继承Thread类的方式:
package com.test.threadtest;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
public class SecondActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mThread mthread1 = new mThread("窗口一");
mthread1.start();
mThread mthread2 = new mThread("窗口二");
mthread2.start();
mThread mthread3 = new mThread("窗口三");
mthread3.start();
}
class mThread extends Thread{
private int ticketsCont = 5;
private String name;
public mThread(String name) {
this.name = name;
}
@Override
public void run() {
while(ticketsCont>0){
ticketsCont--;
System.out.println(name+"卖了一张票,剩余票数为:"+ticketsCont);
}
}
}
}
结果截图:
造成这种结果差异的原因分析:
实现Runnable接口的代码中,创建的三个线程中传递的是同一个Runnable对象,所引用的是同一个资源对象。
继承Thread类的代码中,创建的三个线程对象,每个线程对象中的资源也是独立的,并不是共享的。
线程的生命周期:
从图中可以看出线程的一生中一共有5种状态,分别是:
(1)新建状态: 也就是new出一个线程对象,新建一个线程对象,如:Thread thread = new Thread();
(2)就绪状态: 创建了线程对象后,调用了线程的start()方法,此时线程只是进入了线程队列,等待获取CPU服务,具备了运行条件,但并不一定已经开始运行了。
因为这个时候CPU有可能正在执行其他的线程,而我们当前的这个线程可能要等待,当获取到了CPU服务才会进入运行状态。
(3)运行状态: 处于就绪状态的线程,一旦获取了CPU资源,便进入到运行状态,开始执行run()方法里面的逻辑。
(4)终止状态: 线程的run()方法执行完毕,或者线程调用了stop()方法,线程便进入终止状态。
(5)阻塞状态: 一个正在run()方法中执行的线程在某些情况下,由于某种原因而暂时让出了CPU资源,暂停了自己的执行,便进入了阻塞状态,如调用了sleep()方法。
也就是说,如果在run()方法中调用sleep()方法,就会休眠自己进入阻塞状态,也就会把自己所占用的CPU资源让出来,让其他线程去获取CPU资源,当休眠时间过了之后,线程就会解除阻塞状态进入就绪状态,等待获取CPU资源。
守护线程:
Java中线程有两类:
(1)用户线程:运行在前台,执行具体的任务。
如:程序的主线程,连接网络的子线程等都是用户线程
(2)守护线程:运行在后台,为其他前台的用户线程服务。
特点:
一旦所有用户线程都结束运行,守护线程会随JVM虚拟机一起结束工作。
应用:
数据库连接池中的检测线程,JVM虚拟机启动后的监测线程
最常见的守护线程:垃圾回收线程
将线程设置为守护线程的方法:
可以通过调用Thread类的setDaemon(true)方法来设置当前的线程为守护线程。
使用守护线程的注意事项:
1.setDaemon(true)必须在start()方法之前调用,否则会抛出IllegalThreadStateException异常。
2.在守护线程中产生的新线程也是守护线程。
3.不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑。原因是:当我们在守护线程中执行着读写操作,当读写操作执行到一半的时候,所有的用户线程都退出来了,那么守护线程就会随JVM虚拟机一起结束工作,然而读写操作还没进行完所以程序就崩溃了。
下面用一个例子解释主线程与守护线程之间的关系:(守护线程在某一个时间内不停的往一个文件中写数据,主线程则阻塞等待键盘的输入,一旦主线程获得了用户的输入阻塞就会解除,主线程继续运行直到结束,而一旦主线程结束守护线程就会销毁,但是守护线程中的写入操作不一定就执行完毕了)
源码:
package com.test.test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
System.out.println("进入了主线程"+Thread.currentThread().getName());
DaemonThread daemonThread = new DaemonThread();
Thread thread = new Thread(daemonThread);
thread.setDaemon(true);
thread.start();
Scanner sc = new Scanner(System.in);
sc.next();
System.out.println("退出了主线程"+Thread.currentThread().getName());
}
}
class DaemonThread implements Runnable{
private String filePath = "C:/Users/Administrator/Desktop/daemon.txt";
@Override
public void run() {
System.out.println("进入了守护线程"+Thread.currentThread().getName());
writeToFile();
System.out.println("退出了守护线程"+Thread.currentThread().getName());
}
private void writeToFile() {
OutputStream os = null;
File filename = new File(filePath);
try {
os = new FileOutputStream(filename,true);
int count = 0;
while(count<999){
os.write(("\r\nword"+count).getBytes());
System.out.println("守护线程"+Thread.currentThread().getName()+"向文件中写入了word"+count++);
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
结果截图:
生成的文件内容截图:
通过这个例子:我们可以看到这个程序中只有一个用户线程也就是主线程,当主线程结束的时候,守护线程就会自动销毁(结果就是造成写入的文件不完成)。
通过jstack生成线程快照
jstack的路径位置:
位于jdk/bin目录下
作用:
生成JVM当前时刻线程的快照(threaddump,即当前进程中所有线程 的信息)
目的:
帮助定位程序问题出现的原因,如:长时间停顿,CPU占用率过高等。
使用方法:
在命令提示符中输入jstack,就会显示出jstack的使用帮助。
如图:
这里的-l可有可无,如果有就会打印出线程的一些锁的信息
pid:就是任务管理其中进程的标号
如图:当我启动刚才的程序的时候进程中就会多出一个javaw.exe的进程
图中可以看到PID一项,这里的PID就是我启动的上边的程序进程的标号。
使用jstack运行该程序的PID。如图:
说明:
绿色框中的是进程中其中一个线程的信息。绿框下面的每一段都表示一个现成的信息。
该图中线程状态的说明图:
该图中阻塞状态说明:
Blocked:如果一个线程正在等待一个监视器的锁,那个该线程就处于Blocked状态。eg: 被synchronized阻塞的线程
Waiting:如果一个线程正在无限期的等待另一个线程执行任务,那么这个线程就处于Waiting状态。eg:当线程调用join()方法的时候就会有个线程进入Waiting状态。
Timed_waiting:如果一个线程在指定的时间内等待另一个线程执行任务,那么这个线程就处于Timed_waiting状态。eg:当程序调用了sleep()方法,并在sleep()方法中指定了休眠时间(eg:sleep(1000)形式),那么这个线程就进入了Timed_waiting状态。
所以守护线程处于Timed_waiting状态的原因是,我们在守护线程中调用了sleep(1000)方法。如图:
继续解释命令提示符中的内容
继续查看主线程信息:
<script type="text/javascript">
$(function () {
$('pre.prettyprint code').each(function () {
var lines = $(this).text().split('\n').length;
var $numbering = $('<ul/>').addClass('pre-numbering').hide();
$(this).addClass('has-numbering').parent().append($numbering);
for (i = 1; i <= lines; i++) {
$numbering.append($('<li/>').text(i));
};
$numbering.fadeIn(1700);
});
});
</script>
版权声明:本文为博主原创文章,未经博主允许不得转载。
分享到:
相关推荐
计算机后端-Java-Java核心基础-第20章 多线程 09. 同步方法处理实现Runnable的线程安全
在最近的java学习中,写了一些小例子,都是一些常用知识,上传希望大家下载,共同学习。
计算机后端-Java-Java核心基础-第20章 多线程 07. 同步代码块处理实现Runnable的线程安全问题.avi
本示例 演示采用线程的方式在后台执行任务 要在Android系统中创建和启动线程,与传统的Java程序相同,首先要创建一个Thread对象,使用Thread类的start方法启动线程 启动后将会执行Runnable接口的run方法
在学习Java过程中,自己收集了很多的Java的学习资料,分享给大家,有需要的欢迎下载,希望对大家有用,一起学习,一起进步。
Java多线程--创建多线程的基本方式二:实现Runnable接口
1. 写两个线程,一个线程打印1-52,另一个线程打印字母A-Z。打印顺序为12A34B56C……5152Z。 学习线程之间的通信协调关系。 2. 使用 runnable 接口实现按两... 使用继承Thread类方式和实现Runnable接口两种方式实现。
在Java中创建线程有两种方法:使用Thread类和使用Runnable接口。在使用Runnable接口时需要建立一个Thread实例。因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例。
1、Java有两种实现多线程的方式:通过Runnable接口、通过Thread直接实现,请掌握这两种实现方式,并编写示例程序。 2、多线程是并发执行的,交替占有cpu执行,请编写示例程序,并观察输出结果。 3、采用线程同步方法...
我相信对初学者这是很有帮助的,了解两中实现多线程的方法的不同这处
在Java中只支持单继承,因此通过继承Thread类创建线程有一定的局限性,这时可以使用另一种方式,即实现Runnable接口来创建线程。通过这种方式需要在Thread(Runnable target)的构造方法中,传递一个实现了Runnable...
本文首先介绍了线程的有关概念,接着介绍了线程的生命期及其状态间的转换,多线程的调度 原则,线程的死锁,详细阐述了多线程的两种实现方法: 由Thread类派生子类;实现Runnable接口
分别用Thread类和Runnable接口实现的阶乘的计算过程及结果的显示(本来一开始设置的不要积分,不知道为啥变得需要积分了,故此调整一下)
thread 线程类 实现runnable接口
多线程的学习的笔记 Thread1:继承Thread类 * Thread2:继承Runnable接口 * * ThreadMethod:一些Thread常见方法 * ThreadTestDome1 练习:创建两个线程 其中一个线程遍历100以内的偶数 另一个遍历一百内奇数 ...
一个简单的多线程代码示例,Java实现,用于实现同一时刻,只允许一个线程调用执行的代码块或类,即synchronized的如何使用(多线程实现),实现 Runnable
Java中Runnable和Thread的区别
C#多线程,System.Threading.Thread类,线程同步等
使用java.lang.Thread类或者java.lang.Runnable接口编写代码来定义、实例化和启动新线程。 一个Thread类实例只是一个对象,像Java中的任何其他对象一样,具有变量和方法,生死于堆上。 Java中,每个线程都有一个...
java多线程runnable实例,经过测试的,可以直接运行