进程是系统进行资源分配和调用的独立单元,每一个进程都有它的独立内存空间和系统资源。
一个进程包括由操作系统分配的内存空间,包含一个或多个线程。
单进程和多进程操作系统的区别
单进程操作系统:dos(一瞬间只能执行一个任务)
多进程单用户操作系统:Windows(一瞬间只能执行多个任务)
多进程多用户操作系统:Linux
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。
线程和进程的关系
线程是进程里面的一条执行路径。同一个进程里的每条线程共享进程里面的内存空间和系统资源
一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
进程里只有一条线程的情况下,这条线程就叫做主线程
进程里有多条线程的情况下,只有一条线程叫做主线程
线程是在进程里的,他们是包含关系
线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。保持这个状态直到程序 start() 这个线程。
就绪状态:
新建线程对象后,调用该线程的start()方法之后,该线程进入就绪状态。就绪状态的线程将进入线程队列排队,等待JVM里线程调度器的调度。
运行状态:
当就绪状态的线程被调用并获得 CPU 资源时,线程就进入了运行状态。此时,自动调用该线程对象的run()方法。run()方法定义了该线程的操作和功能。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
阻塞状态:
如果一个正在执行的线程调用了sleep()、wait()等方法,将让出CPU并暂时中止自己的执行,该线程进入阻塞状态。阻塞时,线程不能进入排队队列,只有当引起阻塞的原因被消除后,线程才可以重新进入就绪状态。可以分为三种:
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到死亡状态。处于死亡状态的线程不具有继续运行的能力。
Java 提供了三种创建线程的方法
创建Task类,实现Runnable接口中的run方法
//任务类 1.实现Runnable接口 ,作为线程的实现类
class Task implements Runnable{
//当前线程抢到cpu资源后,就会执行run方法
@Override // 2.重写run()方法 作为线程的操作主体
public void run() {
System.out.println("任务类线程抢到资源了");
}
}
public class Test {
public static void main(String[] args) {
//4.创建线程对象 3.创建实现类对象
Thread thread = new Thread(new Task());
//启动线程:该线程抢到CPU资源后,才会运行run方法
thread.start();// 5.调用start()方法 启动线程
}
}
演示:多线程抢夺系统资源
//实现Runnable接口
class MyThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
//获取当前线程的名称
System.out.println(Thread.currentThread().getName() + "运行, i=" + i);
}
}
}
public class ThreadDemo {
public static void main(String args[]) {
MyThread my = new MyThread(); //定义Runnable子类对象
//系统自动设置线程名称
new Thread(my).start();
//手动设置线程名称
new Thread(my, "线程A").start();
}
}
//Thread-0运行, i=0
//线程A运行, i=0
//Thread-0运行, i=1
//线程A运行, i=1
//Thread-0运行, i=2
//线程A运行, i=2
两个线程对象是交错运行的,哪个线程对象抢到了 CPU 资源,哪个线程就可以运行,所以程序每次的运行结果是不一样的,在线程启动虽然调用的是 start() 方法,但实际上调用的却是 run() 方法定义的主体。
创建MyThread类,继承Thread类,重写run方法(run() 可以调用其他方法,使用其他类,并声明变量,就像主线程一样。)
//线程类 1.继承Thread类
class MyThread extends Thread{
//当前线程抢到cpu资源后,就会自动执行run方法
@Override // 2.重写run()方法
public void run() {
System.out.println("线程类线程抢到资源了");
}
}
public class Test {
public static void main(String[] args) {
// 3.创建子类线程对象
MyThread thread = new MyThread();
//启动线程
thread.start();// 4.调用start()方法
}
}
Thread 类和 Runnable 接口
public class Thread extends Object implements Runnable
Thread 类也是 Runnable 接口的子类,但在Thread类中并没有完全实现 Runnable 接口中的 run() 方法
//下面是 Thread 类的部分源码
Private Runnable target;
public Thread(Runnable target,String name){
init(null,target,name,0);
}
...
public void run(){
if(target!=null){
target.run();
}
}
从源码中可以发现,在 Thread 类中的 run() 方法调用的是 Runnable 接口中的 run() 方法,也就是说此方法是由 Runnable 子类完成的,所以如果要通过继承 Thread 类实现多线程,则必须覆写 run()
如果一个类继承 Thread类,则不适合于多个线程共享资源,而实现了 Runnable 接口,就可以方便的实现资源的共享。
创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值 。
创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。
public class CallableTest implements Callable<Integer> {
public static void main(String[] args) {
CallableTest callableTest = new CallableTest();
//创建一个FutureTask ,它将在运行时执行给定的Callable
FutureTask<Integer> futureTask = new FutureTask<>(callableTest);
//主线程的名称:main
System.out.println("主线程的名称:" + Thread.currentThread().getName());
//Thread(Runnable target, String name)
new Thread(futureTask, "有返回值的线程").start();
try {
//子线程的返回值:10
System.out.println("子线程的返回值:" + futureTask.get());
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public Integer call() throws Exception {
int i = 10;
//子线程的名称:有返回值的线程
System.out.println("子线程的名称:" + Thread.currentThread().getName());
return i;
}
}
列出被Thread对象调用 的一些重要方法:
方法 | 描述 |
---|---|
void start() | 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 |
void run() | 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。 |
void setName(String name) | 改变线程名称,使之与参数 name 相同。 |
void setPriority(int priority) | 更改线程的优先级。 |
void setDaemon(boolean on) | 将该线程标记为守护线程或用户线程。 |
void join(long millisec) | 等待该线程终止,时间最长为 millis 毫秒。(让一个线程强制运行,线程强制运行期间,其他线程必须等待此线程终止之后才可以继续执行) |
void interrupt() | 中断线程。 |
final boolean isAlive() | 测试线程是否处于活动状态。 |
public class Task implements Runnable{
@Override
public void run() {
System.out.println("run方法被调用了");
}
}
public class Test {
public static void main(String[] args) {
//创建子线程
Thread thread = new Thread(new Task());
//更改线程的优先级
thread.setPriority(Thread.MAX_PRIORITY);//10
//参数为true时 将此线程标记为守护线程
thread.setDaemon(false);
//更改线程的名称为:Hello线程
thread.setName("Hello线程");
//启动线程
thread.start();
//中断此线程
thread.interrupt();
//测试此线程是否存活
System.out.println(thread.isAlive());
try {
//等待这个线程死亡,再向下执行
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
列出Thread类的静态方法 :
方法 | 描述 |
---|---|
void yield() | 放弃,向调度程序提示当前线程放弃其当前对处理器的使用。回到就绪状态,竞争下一次时间片。 |
void sleep(long millisec) | 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 |
boolean holdsLock(Object x) | 当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。 |
Thread currentThread() | 返回对当前正在执行的线程对象的引用。 |
void dumpStack() | 将当前线程的堆栈跟踪打印至标准错误流。 |
Thread.sleep(毫秒);
此方法为静态方法,写在哪个线程中,哪个线程就休眠
需求:编写一个抽取学生回答问题的程序,要求倒数三秒后输出被抽中的学生姓名
public static void main(String[] args) throws InterruptedException {
//创建随机数对象
Random ran = new Random();
String[] names = {"中野三玖", "志田黑羽", "樱泽墨", "莱卡"};
//返回一个伪随机、均匀分布的int值,介于 0(含)和指定值(不含)之间
int index = ran.nextInt(names.length);
//3秒倒计时
for (int i = 3; i > 0; i--) {
System.out.println(i);
//休眠1000毫秒
Thread.sleep(1000);
}
System.out.println(names[index]);
}
//3
//2
//1
//樱泽墨
Thread.yield();
此方法为静态方法,此方法写在哪个线程中,哪个线程就礼让
将一个线程的操作暂时让给其他线程执行(指当前线程退出CPU资源,并转到就绪状态,接着再抢)
需求:创建两个线程A,B,分别各打印1-100的数字,其中B一个线程,每打印一次,就礼让一次
class A extends Thread {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println("A:" + i);
}
}
}
class B extends Thread {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println("B:" + i);
//礼让:让当前线程退出CPU资源
Thread.yield();
}
}
}
public class Test {
public static void main(String[] args) {
A a = new A();
B b = new B();
a.start();
b.start();
}
}
t.join();
在线程操作中,可以使用 join() 方法让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行
需求:主线程和子线程各打印15次,从1开始每次增加1,当主线程打印到5之后,让子线程先打印完再打印主线程
class MyThread extends Thread {
@Override
public void run() {
for (int i = 1; i <= 15; i++) {
System.out.println("子线程:" + i);
}
}
}
public class Test01 {
public static void main(String[] args) throws InterruptedException {
//创建子线程
MyThread t = new MyThread();
t.start();
for (int i = 1; i <= 15; i++) {
System.out.println("主线程:" + i);
if (i == 5) {
//将t线程加入到当前线程强制运行
t.join();
}
}
}
}
t.interrupt();
当一个线程运行时,另外一个线程可以直接通过interrupt()方法中断其运行状态。
需求:子线程循环打印人名,主线程0.1秒后中断子线程打印
class MyThread extends Thread {
@Override
public void run() {
//获取当前线程状态 true - 线程终止 false - 线程存活
while (!Thread.currentThread().isInterrupted()) {
System.out.println("樱泽墨");
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
MyThread t = new MyThread();
t.start();
Thread.sleep(100);
t.interrupt();//改变线程状态
}
}
t.setDaemon(true);
守护线程 默默守护着前台线程,当所有的前台线程都消亡后,守护线程会自动消亡
注意:垃圾回收器就是守护线程
class MyThread extends Thread {
@Override
public void run() {
while (true) {
System.out.println("守护线程,正在默默守护着前台线程");
try {
Thread.sleep(1001);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
MyThread t = new MyThread();
//将t线程设置为守护线程
t.setDaemon(true);
t.start();
for (int i = 1; i <= 2; i++) {
System.out.println("主线程 :" + i);
Thread.sleep(1000);
}
}
}
//主线程 :1
//守护线程,正在默默守护着前台线程
//主线程 :2
//守护线程,正在默默守护着前台线程
//
//进程已结束,退出代码为 0
未完待续。。。。。
在 Java 的线程操作中,所有的线程在运行前都会保持在就绪状态,那么此时,哪个线程的优先级高,哪个线程就 有可能 会先被执行。
class MyThread implements Runnable { // 实现Runnable接口
@Override
public void run() { //重写run()方法
//取得当前线程的名字
System.out.println(Thread.currentThread().getName() + "运行");
}
}
public class ThreadPriorityDemo {
public static void main(String args[]) {
// 实例化线程对象
Thread t1 = new Thread(new MyThread(), "线程A");
Thread t2 = new Thread(new MyThread(), "线程B");
Thread t3 = new Thread(new MyThread(), "线程C");
t1.setPriority(Thread.MAX_PRIORITY); //优先级最高 10
t2.setPriority(Thread.NORM_PRIORITY); //优先级中等 5
t3.setPriority(Thread.MIN_PRIORITY); //优先级最低 1
// 启动线程
t1.start();
t2.start();
t3.start();
}
}
//线程B运行
//线程A运行
//线程C运行
从程序的运行结果中可以观察到,线程将根据其优先级的大小来决定哪个线程会先运行,但是注意并非优先级越高就一定会先执行,哪个线程先执行将由 CPU 的调度决定。