程序进入内存中运行就变成一个进程,进程具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位。
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1–n个线程。(进程是资源分配的最小单位)
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
多进程是指操作系统能同时运行多个任务(程序)。
多线程是指在同一程序中有多个顺序流在执行。
在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口.(其实准确来讲,应该有三种,还有一种是实现Callable接口,并与Future、线程池结合使用
多进程的目的是提高CPU的使用率,提高工作效率,多个工作可以同时进行。
在一个进程里,会有多个线程并发执行抢夺cpu的使用权,抢夺概率是随机的。。
注:JVM也是多线程程序,因为其自带垃圾回收器,确保程序不会轻易的造成内存溢出看,所以JVM至少开启两条线程。一是程序会执行main主线程;二是垃圾回收器线程的运行确保程序不会内存异常。
重点:如何实现多线程程序?
* 要实现多线程程序,必须创建一个进程,
* 创建进程需要调用系统资源进行创建,但是Java语言是不能直接调用系统资源
* C/C++语言是可以创建系统资源,然后使用Java语言掉C/C++已经封装好的东西,
多线程程序实现方式1:
1)自定一个类:MyThread 继承自Thread类
2)在MyThread类中重写Thread类中的run()
3)在主线程中,创建该类的实例对象,启动线程
测试类
package Day20_DuoJinCheng;
/**
* @author Aoman_Hao
*/
public class Test1 {
public static void main(String[] args) {
//创建进程
//无参构造
UseThread U1 = new UseThread();
U1.setName("U1");
//有参构造
UseThread U2 = new UseThread("U2");
U1.start();
U2.start();
}
}
对象类:
package Day20_DuoJinCheng;
/**
* @author Aoman_Hao
*/
public class UseThread extends Thread {
//无参构造
public UseThread() {
super();
}
//有参构造
public UseThread(String name) {
super(name);
}
@Override
public void run() {
for(int i=1;i<20;i++){
System.out.println("进程"+Thread.currentThread().getName()+"的数字:"+i);
}
}
}
输出:进程U2的数字:1
进程U1的数字:1
进程U2的数字:2
进程U1的数字:2
进程U2的数字:3
进程U1的数字:3
进程U2的数字:4
进程U1的数字:4
进程U1的数字:5
进程U1的数字:6
进程U1的数字:7
进程U1的数字:8
进程U1的数字:9
进程U1的数字:10
进程U1的数字:11
进程U1的数字:12
进程U1的数字:13
进程U1的数字:14
进程U1的数字:15
进程U1的数字:16
进程U1的数字:17
进程U1的数字:18
进程U1的数字:19
进程U2的数字:5
进程U2的数字:6
进程U2的数字:7
进程U2的数字:8
进程U2的数字:9
进程U2的数字:10
进程U2的数字:11
进程U2的数字:12
进程U2的数字:13
进程U2的数字:14
进程U2的数字:15
进程U2的数字:16
进程U2的数字:17
进程U2的数字:18
进程U2的数字:19
多线程构建方法2:
1)自定义一个类MyRunnable,该类实现Runnable接口
2)实现该接口中的run()方法
3)在主线程中创建该类的实例对象,
4)创建Thread类对象,将3)创建的这个对象作为参数进行传递
5)分别启动线程
测试类:
package Day20_DuoJinCheng;
/**
* @author Aoman_Hao
*/
public class RunTest {
public static void main(String[] args) {
//创建MyRunnable实例对象
UseRunnalbe U1 = new UseRunnable();
//创建Thread有参构造类对象
Thread T1 = new Thread(U1);
T1.setName("T1");
//一步走
Thread T2 = new Thread(U1,"T2");
//启动多线程
T1.start();
T2.start();
}
}
实例对象类:
package Day20_DuoJinCheng;
/**
* @author Aoman_Hao
*/
public class UseRunnable implements Runnable {
@Override
public void run() {
for(int i=1;i<20;i++){
//间接的使用Thread类的静态功能得到线程名称
System.out.println(Thread.currentThread().getName()+"数字:"+i);
}
}
}
输出:
T1数字:1
T2数字:1
T1数字:2
T2数字:2
T1数字:3
T2数字:3
T1数字:4
T2数字:4
T1数字:5
T2数字:5
T1数字:6
T2数字:6
T1数字:7
T2数字:7
T1数字:8
T2数字:8
T1数字:9
T2数字:9
T1数字:10
T2数字:10
T1数字:11
T2数字:11
T1数字:12
T2数字:12
T1数字:13
T2数字:13
T1数字:14
T2数字:14
T1数字:15
T2数字:15
T1数字:16
T2数字:16
T1数字:17
T2数字:17
T1数字:18
T2数字:18
T1数字:19
T2数字:19
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
总结:
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类
public final void join() –throws InterruptedException等待该线程终止。
public final int getPriority()–返回线程的优先级。
public static void yield()–暂停当前正在执行的线程对象,并执行其他线程。
测试类
package Day20_DuoJinCheng;
/**
* @author Aoman_Hao
*/
public class Demo {
public static void main(String[] args) {
//实现Runnable接口的类
UseRunnable ur = new UseRunnable();
//创建多线程的类
Thread T1 = new Thread(ur,"T1");
Thread T2 = new Thread(ur,"T2");
Thread T3 = new Thread(ur,"T3");
//获取优先级等级数
int p2_pri = T2.getPriority();
System.out.println(p2_pri);
//设置优先级等级数
T2.setPriority(10);
System.out.println(T2.getPriority());
//启动多线程
T1.start();
//join 等待进程终止
try {
T1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
T2.start();
T3.start();
}
}
对象类:
package Day20_DuoJinCheng;
/**
* @author Aoman_Hao
*/
public class UseRunnable implements Runnable {
@Override
public void run() {
for(int i=1;i<20;i++){
//间接的使用Thread类的静态功能得到线程名称
System.out.println(Thread.currentThread().getName()+"数字:"+i);
//public static void yield()暂停当前正在执行的线程对象,并执行其他线程。
Thread.yield();
}
}
}
public final void setDaemon(boolean on) –on指定true,就是设置守护线程…
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。
jvm自动退出,对于主线程的数据如果直接输出完毕,对于两个守护线程来说不会立即消失,Jvm等会就自动退出.
线程停止:
public final void stop():强迫线程停止执行
public void interrupt()中断线程。 表示中断线程一种状态
- setDeamon(boolean on):(用的多)
- sleep():线程睡眠 (用的多)
- wait():线程等待
sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会被执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
测试类:
package Day20_Duo;
/**
* @author Aoman_Hao
*/
public class Demo2 {
public static void main(String[] args) {
CopyUseRunnable C = new CopyUseRunnable();
Thread t1 = new Thread(C,"T1");
Thread t2 = new Thread(C,"T2");
Thread t3 = new Thread(C,"T3");
//设置t1为守护线程,主程序计算完,守护线程的数据慢慢运行
t1.setDaemon(true);
//stop过时了,依旧可用,现在不推荐这种用法
// t2.stop();
//public void interrupt()中断线程。 表示中断线程一种状态
t2.interrupt();
t1.start();
t2.start();
t3.start();
}
}
对象类:
package Day20_Duo;
/**
* @author Aoman_Hao
*/
public class CopyUseRunnable implements Runnable {
@Override
public void run() {
for(int i=1;i<20;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//间接的使用Thread类的静态功能得到线程名称
System.out.println(Thread.currentThread().getName()+"数字:"+i);
}
}
}
输出:
T3数字:1
T1数字:1
T2数字:1
T3数字:2
T2数字:2
T1数字:2
T1数字:3
T2数字:3
T3数字:3
T1数字:4
T2数字:4
T3数字:4
T2数字:5
T1数字:5
T3数字:5
T2数字:6
T1数字:6
T3数字:6
T2数字:7
T1数字:7
T3数字:7
T1数字:8
T2数字:8
T3数字:8
T1数字:9
T2数字:9
T3数字:9
T1数字:10
T2数字:10
T3数字:10
T1数字:11
T2数字:11
T3数字:11
T2数字:12
T1数字:12
T3数字:12
T1数字:13
T2数字:13
T3数字:13
T1数字:14
T2数字:14
T3数字:14
T1数字:15
T2数字:15
T3数字:15
T1数字:16
T2数字:16
T3数字:16
T1数字:17
T2数字:17
T3数字:17
T1数字:18
T2数字:18
T3数字:18
T2数字:19
T1数字:19
T3数字:19
t2运行stop,t2线程直接结束,不在运行,抢CPU。