上一节我们讲解了进程和线程的区别,概念比较繁重,可能读者会出现大脑昏晕的状态,接下来我就实例讲解一下具体的使用,供大家更加深入的理解概念:
(一)操作进程:在Java代码中如何运行一个进程,主要有两种方式:
实现功能:在Java程序中如何开启一个进程:运行记事本程序!
方式一:Runtime类的exec()方法即可开启一个进程(每一个应用程序中都会有一个Runtime类的实例对象,这个类构造函数私有,对象单例)
//方式一:使用Runtime类的exec()方法
Runtime runtime = Runtime.getRuntime();
try {
runtime.exec("notepad");
} catch (IOException e) {
e.printStackTrace();http://blog.csdn.net/super_yc/article/details/73294203
}
方式二:ProcessBuilder 类中的start()方法就可以开启一个进程:
//方式二:ProcessBuilder的start()方法
ProcessBuilder pb = new ProcessBuilder("notepad");
try {
pb.start();
} catch (IOException e) {
e.printStackTrace();
}
(二)操作线程,创建和启动线程在传统上有两种方式:
方式一:继承Thread类(本质上Thread 类也实现了Runnable 接口)
步骤:
1)定义一个类继承于java.lang.Thread类;
2)在SubThread类中覆盖Thread类中的run()方法;
3)在run()方法中编写需要执行的操作------->run()方法里即为线程执行体;
4)在main()方法(线程)中,创建线程对象,并启动线程!
创建线程类对象 SubThread st = new SubThread();
调用线程对象中的start()方法st.start();//启动一个线程
注意:在创建线程对象后,直接调用run()方法,依然只有一个线程,就是主线程,并没有开启一个新线程;和普通方法调用等同,依然串行执行!
方式二:实现Runnable接口;
步骤:
1)定义一个类实现接口java.lang.Runnable接口;
2)在Sub类中覆盖Runnable接口中的run()方法;
3)在run()方法中编写需要执行的操作------->run()方法里,线程执行体;
4)在main()方法中创建线程对象,并且启动线程!
创建线程类对象: Thread t = new Thread( new Sub() );
调用线程对象的start()方法:t.start();
使用实现Runnable接口创建线程,先判断target不为空,直接调用实现类中的run()方法!(运行时,动态绑定,多态实现!)
@Override
public void run() {
if (target != null) {
target.run();
}
}
注意:默认的Thread.run()就是直接调用内部的Runabble接口,因此使用Runnable接口告诉线程该做什么,比较合理。要使用Thread(Runnable target)的构造函数,实现Runnable接口的类,想要开启一个线程,必然要调用start()方法,但Runnable接口中没有此方法,所以必须要借助Thread类的API来启动线程!(Thread本质上也是Runabble接口的实现类)
【示例】
/*
* 案例:存在50个苹果,现在有请三个童鞋(小A,小B,小C)上台表演吃苹果,因为A,B,C三个人可以同 * 时吃苹果,此时得使用多线程技术来实现这个案例,
* 分析:可以定义三个线程对象,并启动线程,每个同学吃苹果的时候:先展示自己拿到手上苹果的编 * 号,如1,2,3,4.....50,再吃掉苹果 (意味着苹果总数减少一个)
*/
方式一:可以使用继承Thread类的方式来实现(继承执行失败,不能实现其功能!)
//方式一:可以使用继承Thread类的方式来实现;
public class EatAppleGame {
public static void main(String[] args) {
//创建申请那个线程(同学),吃苹果
Person p1 = new Person("小A");
Person p2 = new Person("小B");
Person p3 = new Person("小C");
p1.start();
p2.start();
p3.start();
}
}
class Person extends Thread{
//表示苹果总数
private int num = 50;
public Person(String name) {
super(name);
}
public void run(){
for (int i = 0; i < 50; i++) {
if(num > 0){
System.out.println(super.getName() + "吃了编号为:" + num-- + "的苹果!");
}
}
}
}
方式二:通过继承Thread类来实现临界资源的共享:(可以实现资源共享,Thread类本质上也实现了Runnable接口)
//方式二:可以使用继承Thread类的方式来实现;
public class EatAppleGame {
public static void main(String[] args) {
//创建申请那个线程(同学),吃苹果,创建三个对象,资源不能共享
Person p = new Person();
new Thread(p,"小A").start();
new Thread(p,"小B").start();
new Thread(p,"小C").start();
}
}
class Person extends Thread{
//表示苹果总数
private int num = 50;
public void run(){
for (int i = 0; i < 50; i++) {
if(num > 0){
System.out.println(Thread.currentThread().getName() + "吃了编号为:" + num-- + "的苹果!");
}
}
}
}
方式三:可以使用实现Runnable接口的方式来实现(实现接口成功,可实现其功能!)
//方式三:可以使用实现Runnable接口的的方式来实现;
public class EatAppleImpl {
public static void main(String[] args) {
//创建申请那个线程(同学),吃苹果
Apple a = new Apple();
//三个线程共享Apple中的成员变量资源(num资源)
new Thread(a,"小A").start();
new Thread(a,"小B").start();
new Thread(a,"小C").start();
}
}
class Apple implements Runnable{
private int num = 50;
public void run(){
for (int i = 0; i < 50; i++) {
if(num > 0){
String threadName = Thread.currentThread().getName();//获取当前的线程,进而获取线程名称
System.out.println(threadName + "吃了编号为:" + num-- + "的苹果!");
}
}
}
}
对应上述代码,出现这种结果情况:
小B吃了编号为:50的苹果!
小A吃了编号为:49的苹果!
小C吃了编号为:49的苹果!
小A吃了编号为:48的苹果!
小B吃了编号为:46的苹果!
小C吃了编号为:47的苹果!
以及这种情况:
小A吃了编号为:3的苹果!
小C吃了编号为:2的苹果!
小C吃了编号为:1的苹果!
小B吃了编号为:-1的苹果!
小A吃了编号为:0的苹果!
这两种结果会导致多想访问统一资源的安全问题!
重点:继承方式和实现方式的区别:
继承方式:
1)Java中类是单继承的,如果继承了Thread类,该类就不能再有其他的直接父类;
2)从操作上分析,继承方式更加简单,获取线程名称简单!(操作简单);
3)从多线程共享同一资源上分析,继承Thread类方式一是不能实现资源的共享,但是方式二是可以实现的(本质上Thread类也实现类Runnable接口)
实现方式:
1)Java中类可以实现接口,此时该类还可以继承其他的类,并且还可以实现其他的接口(设计上更加优雅和优秀);
2)从操作上分析,实现方式稍微有点复杂,获取线程的名称也比较复杂,得使用Thread,currentThread()来获取当前的线程,进而获取线程的名称;
3)从多线程共享同一资源上分析,实现接口可以做到多个线程共享资源!(是否共享同一个资源);
4)默认的Thread.run()就是直接调用内部的Runabble接口,因此使用Runnable接口告诉线程该做什么,比较合理。
下一篇对应此例子,实现多线程访问共同资源的安全问题:http://blog.csdn.net/super_yc/article/details/73294203