1、进程
-
进程是正在运行的程序;
-
是系统进行资源分配和调用的独立单位
-
每一个进程都有自己的内存空间和系统资源
2、线程
2.1、线程的说明
线程:是进程中的单个顺序控制流,是一条执行路径
-
单线程:一个进程如果只有一条执行路径,则称为单线程程序
-
多线程:一个进程如果有多条执行路径,则称为多线程程序
2.2、多线程的实现方式:
方式一:继承Thread类
-
1:定义一个类MyThread继承Thread类
-
2:在MyThread类中重写run()方法
-
3:创建My Thread类的对象
-
4:启动线程
两个小问题:
-
a、为什么要重写run()方法?
因为run()是用来封装被线程执行的代码
-
b、run()方法和start()方法的区别?
run():封装线程执行的代码,直接调用,相当于普通方法的调用
start():启动线程;然后由JVM调用此线程的run()方法
方式二:实现Runnable接口
-
定义一个类MyRunnable实现Runnable接口
-
在MyRunnable类中重写run()方法
-
创建MyRunnable类的对象
-
创建Thread类的对象,把MyRunnable对象作为构造方法的参数
-
启动线程
相比继承Thread类,实现Runnable接口的好处
-
在实现多线程的时候还可以去继承其他的类
-
避免了Java单继承的局限性
-
适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想
代码实现:
创建一个MyRunnable实现Runnable接口
package 线程;
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//这里不能直接使用getName()来获取线程名字,因为这只是实现了Runnable接口而已
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
}
}
创建一个实现类
package 线程;
/**
*方式二:实现Runnable接口
*
* 定义一个类MyRunnable实现Runnable接口
*
* 在MyRunnable类中重写run()方法
*
* 创建MyRunnable类的对象
*
* 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
*
* 启动线程
*/
public class MyRunnableDemo {
public static void main(String[] args) {
//创建MyRunnable类的对象
MyRunnable mr=new MyRunnable();
//创建Thread类的对象,把MyRunnable对象作为构造方法的参数
Thread t1=new Thread(mr,"张三");
Thread t2=new Thread(mr,"李四");
//启动线程
t1.start();
t2.start();
}
}
2.3、设置和获取线程名字
setName() 方法是用来给线程添加名字的
getName() 方法是用来获取线程的名字的,当没给线程赋值一个名字时,线程是使用默认的名字 Thread-0后面的数字从零开始
例题:
1、创建一个线程类:
package 线程;
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 15; i++) {
System.out.println(getName()+"\t"+i);
}
}
}
2、编写一个实现类:
package 线程;
/**
* 方式1:继承Thread类
* 1:定义一个类MyThread继承Thread类
* 2:在MyThread类中重写run()方法
* 3:创建My Thread类的对象
* 4:启动线程
*/
public class MyThreadDemo {
public static void main(String[] args) {
//currentThread()方法是用来获取当前执行的线程名字
System.out.println(Thread.currentThread().getName());
//创建两个线程对象
MyThread mt1=new MyThread();
MyThread mt2=new MyThread();
//setname()方法给线程赋值名字,若没有赋值名字就会产生默认名字 Thread-第几个,从0开始;
//getname()方法是用来获取线程名字的
//给线程添加名字
mt1.setName("飞机");
mt2.setName("大炮");
//运行线程
mt1.start();
mt2.start();
}
}
2.4、线程的调度
线程有两种调度模型
-
分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
-
抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些
-
Java使用的是抢占式调度模型。
2.5、线程的优先级
Tread类的设置和获取线程优先级的方法
-
getPriority()方法是用来返回此线程的优先级
-
setPriority()方法是用来修改此线程的优先级
注意:
-
线程的优先级默认值是:5,线程优先级的范围是:1-10;
-
线程优先级高仅仅表示线程获取的CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到你想要的效果,不是每一次线程优先级高的都在最前面;
package 线程;
/**
* Thread类中设置线程的优先级
* getPriority()方法是用来返回此线程的优先级
* setPriority()方法是用来修改线程的优先级的
*/
public class MyThreadDemo02 {
public static void main(String[] args) {
//创建两个线程对象
MyThread mt1=new MyThread();
MyThread mt2=new MyThread();
MyThread mt3=new MyThread();
//给线程添加名字
mt1.setName("飞机");
mt2.setName("大炮");
mt3.setName("java");
//getPriority()方法是用来返回此线程的优先级
// System.out.println(mt1.getPriority());
// System.out.println(mt2.getPriority());
// System.out.println(mt3.getPriority());
//setPriority()方法是用来修改线程的优先级的
//线程的默认优先级是 5 ,最大优先级是 10 ,最小优先级是 1
// System.out.println(Thread.MAX_PRIORITY); // 10
// System.out.println(Thread.MIN_PRIORITY); // 1
// System.out.println(Thread.NORM_PRIORITY); // 5
//优先级高是获取CPU资源的概率高,并不是每一次都会在前面
mt1.setPriority(1);
mt2.setPriority(10);
mt3.setPriority(9);
//运行线程
mt1.start();
mt2.start();
mt3.start();
}
}
2.6、线程控制
方法 | 说明 |
---|---|
static void sleep(long millis) | 使当前正在执行的线程停留(暂停执行)指定的毫秒数,是指运行完一个线程后,暂停的秒数 |
void join() | 等待这个线程死亡 |
void setDaemon(boolean on) | 将此线程标记为守护线程,当运行的线程都是守护线程的时,Java虚拟机将退出 |
currentThread() | 设置一个主线程 |
static void sleep(long millis) 方法的使用:
当一个线程执行完之后需要需要等其他线程都执行完之后才能执行,就是需要一个同步线程的时候,可以用来这个方法来设置休息毫秒数来控制;
案例:
1、线程实现类
package 线程;
/**
* sleep方的使用案例
*当线程需要每一个都执行一次的时候可以使用,
* 就是需要线程每个都能执行一次,才能在执行
* 就是要求 曹操 刘备 孙权 当一个执行后,要等其他两个执行完之后才能在执行
*/
public class MyTreadSleep_Demo {
public static void main(String[] args) {
//创建两个线程对象
MyTreadsleep mt1=new MyTreadsleep();
MyTreadsleep mt2=new MyTreadsleep();
MyTreadsleep mt3=new MyTreadsleep();
//setname()方法给线程赋值名字,若没有赋值名字就会产生默认名字 Thread-第几个,从0开始;
//getname()方法是用来获取线程名字的
//给线程添加名字
mt1.setName("刘备");
mt2.setName("曹操");
mt3.setName("孙权");
//运行线程
mt1.start();
mt2.start();
mt3.start();
}
}
2、线程:
package 线程;
public class MyTreadsleep extends Thread {
@Override
public void run() {
for (int i = 0; i < 15; i++) {
System.out.println(getName()+"\t"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
void join() :方法的使用,当需要一个线程执行完,其他线程才能执行时,就可以这个方法;
package 线程;
/**
* join方法的使用案例:
* 当康熙死后,四阿哥和八阿哥才能抢位置
* 及当康熙线程执行完之后,四阿哥和八阿哥两个线程才能执行
*/
public class MyThreadDemo03 {
public static void main(String[] args) {
//创建两个线程对象
MyTreadJoin mt1=new MyTreadJoin();
MyTreadJoin mt2=new MyTreadJoin();
MyTreadJoin mt3=new MyTreadJoin();
//给线程添加名字
mt1.setName("康熙");
mt2.setName("四阿哥");
mt3.setName("八阿哥");
//运行线程
mt1.start();
try {
mt1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
mt2.start();
mt3.start();
}
}
void setDaemon(boolean on)的方法使用案例:
package 线程;
/**
* 守护线程的案例,
* void setDaemon(boolean on)的方法使用案例;
* 当刘备线程执行完之后,关羽和张飞线程就不再执行了,这就需要将关羽和张飞设置为守护线程
*
*/
public class setDaemon_Demo {
public static void main(String[] args) {
//创建两个线程对象
MyTreadJoin mt2=new MyTreadJoin();
MyTreadJoin mt3=new MyTreadJoin();
//给线程添加名字
mt2.setName("关羽");
mt3.setName("张飞");
mt2.setDaemon(true);
mt3.setDaemon(true);
//设置主线程,当主线程执行完成之后,守护线程会在执行一段才会关闭
Thread.currentThread().setName("刘备");
//运行线程
mt2.start();
mt3.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
}
}