程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期
如: 运行中的QQ,运行中的MP3播放器
程序是静态的,进程是动态的
进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。
背景:以单核CPU为例,只使用单个线程先后完成多个任务(调用多个方 法),肯定比用多个线程来完成用的时间更短,为何仍需多线程呢?
多线程程序的优点:
API中创建线程的两种方式:继承Thread类,实现Runnable接口的方式。
使用方式:
我们以“遍历100以内的所有偶数”来举例
// 1.创建一个继承于Thread的子类
class MyThread extends Thread{
//2.重写Run()方法 --> 此线程执行的操作声明在run()方法中
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
//3. 创建Thread子类的对象
MyThread myThread = new MyThread();
//4.通过此对象调用start()“:①启动当前线程 ②调用当前线程的run();
myThread.start();
//问题一:我们不能够直接调用run()的方式启动线程
//myThread.run();
//问题二:在启动一个线程,遍历100以内的偶数。不可以还让已经start()的线程去执行
//myThread.start();
//我们需要重新创建一个线程的对象
MyThread myThread2 = new MyThread();
myThread2.start();
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
System.out.println(Thread.currentThread().getName() + ":" + "hello");
}
}
Thread.currentThread().getName()这个代码意思是获取当前线程的名字,我们从运行结果上来看,我们创建的线程和主线程存在交替执行的情况,是因为主线程和我们创建的线程在相互抢占CPU资源。
这里有几点需要注意的:
这里看起来可能会有些抽象,下面用代码来展示一下,同样是使用遍历100以内偶数这个例子。
package com.demo.thread;
/*
创建多线程的方式二:实现Runnable接口
1. 创建一个实现Runnable接口的类
2. 实现类去实现Runnable中的抽象方法: run();
3. 创建实现类的对象
4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5. 通过Thread类的对象调用start()
*/
class MThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i%2==0){
System.out.println(i);
}
}
}
}
public class ThreadTest2 {
public static void main(String[] args) {
MThread mThread = new MThread();
Thread thread = new Thread(mThread);
thread.start();
}
}
我们可以看下Runnable接口的源码
从运行结果上来看,这两者没什么区别
那么他们两者的区别是什么呢?
下面我们来做一个练习。
class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
class MyThread2 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 1){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread1 m1 = new MyThread1();
MyThread2 m2 = new MyThread2();
m1.start();
m2.start();
}
}
public class ThreadDemo {
public static void main(String[] args) {
//创建Thread类的匿名子类的方式
new Thread(){
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}.start();
new Thread(){
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 1){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}.start();
}
}
测试Thread中的常用方法: 1. start(): 启动当前线程;调用当前的run() 2. run(): 通常需要重新Thread类中的此方法,将创建的线程要执行的操作声明在此方法中 3. currentThread(): 静态方法,返回执行当前代码的线程 4. getName(): 获取当前线程的名字 5. setName(): 设置当前线程的名字 6. yield(): 释放当前cpu执行权 7. join(): 在线程a中调用线程b的join,此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态 8. isAlive(): 判断当前线程是否还存活
前三种在前面已经有了一定的了解,下面我将简单介绍一下后面几种的用法。
getname()和setname()都比较简单,直接通过对象调用即可。
那么yield()方法又是做什么的呢,yield又叫线程让步,暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程,若队列中没有同优先级的线程,忽略此方法。
大家可以在上面的代码中插入看看效果。
join方法就比较好理解了,我们在主线程中插入join方法。
然后我们再来看运行结果
当出现i == 20时,主线程进入了阻塞状态,待Thread-1执行完毕之后,主线程才继续执行。
线程的优先级: 1. MAX_PRIORITY:10 MIN_PRIORITY: 1 NORM_PRIORITY: 5 2.如何获取和设置当前线程的优先级 getPriority(); setPriority(int p); 高优先级要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下被执行。 并不意味着只有当高优先级的线程执行完以后,低优先级线程才执行。
class HelloThread extends Thread{
public HelloThread(String name){
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
// try {
// sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println(Thread.currentThread().getName() + ":" + i + "***" + Thread.currentThread().getPriority());
}
// if (i % 20 == 0){
// this.yield();
// }
}
}
}
public class ThreadMethodTest {
public static void main(String[] args) throws InterruptedException {
HelloThread h1 = new HelloThread("Thread-1");
// h1.setName("线程一");
h1.setPriority(Thread.MAX_PRIORITY);
h1.start();
//给主线程命名
Thread.currentThread().setName("主线程");
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
for (int i = 0; i < 100; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() + ":" + i + "***" + Thread.currentThread().getPriority());
}
// if (i == 20){
// try {
// h1.join();
// }catch (InterruptedException e){
// e.printStackTrace();
// }
// }
}
System.out.println(h1.isAlive());
}
}
我们把分线程的优先级设置为最高,从运行结果来看,并不是等分线程执行完再执行主线程。这也说明了从概率讲高优先级的线程高概率被执行。