多线程
程序、进程、线程的定义
程序:为了实现某个特定的任务,使用某种语言编写的一组指令的集合。
进程:正在硬件上运行的程序
线程:是进程内部的一个顺序控制流,是程序运行调度的最小执行单元,如果一个程序中只有一个线程,这个程序就是单线程程序,有多个线程就是多线程程序,一个进程中至少有一个线程。
为什么要使用多线程
1.充分利用计算机的cpu的处理能力
2.方便业务拆分,提升系统的并发能力和性能
并发容易引发的问题
线程安全:多个线程操作同一个变量,例如买票,不加处理会出现同一张票卖多次
死锁:多个线程因为竞争资源而互相等待,导致程序不能正常运行。
上下文切换:因为java采用的是抢占式的cpu时间调度模型,当一个线程执行途中被另一个线程抢走cpu的执行权,这就是上下文切换,因为上下文切换很占用时间,影响程序响应速度。
实现多线程的方式
1.继承Thread类,重写run方法,调用start方法启动线程
package com.sj.thread;
public class ThreadDemo1 extends Thread {
@Override
public void run() {
for (int i = 0; i <5 ; i++) {
System.out.println("helloWorld");
}
}
}
package com.sj.thread;
public class Demo1Test {
public static void main(String[] args) {
ThreadDemo1 thread1 = new ThreadDemo1();
ThreadDemo1 thread2=new ThreadDemo1();
thread1.start();
thread2.start();
}
}
/*
输出结果:
helloWorld
helloWorld
helloWorld
helloWorld
helloWorld
helloWorld
helloWorld
helloWorld
helloWorld
helloWorld
*/
2.实现runable接口,重写run方法,调用start方法启动线程
package com.sj.thread;
public class ThreadDemo2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("helloWorld");
}
}
}
package com.sj.thread;
public class Demo1Test {
public static void main(String[] args) {
Thread thread1= new Thread(new ThreadDemo2());
Thread thread2= new Thread(new ThreadDemo2());
thread1.start();
thread2.start();
}
}
/*
输出结果:
helloWorld
helloWorld
helloWorld
helloWorld
helloWorld
helloWorld
helloWorld
helloWorld
helloWorld
helloWorld
*/
3.实现callable接口,重写call方法,调用start方法启动线程
package com.sj.thread;
import java.util.concurrent.Callable;
public class ThreadDemo3 implements Callable {
@Override
public Object call() throws Exception {
for (int i = 0; i <5 ; i++) {
System.out.println("helloWorld");
}
return "已经执行了5次";
}
}
package com.sj.thread;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Demo1Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> task1 = new FutureTask(new ThreadDemo3());
FutureTask<String> task2 = new FutureTask(new ThreadDemo3());
new Thread(task1).start();
new Thread(task2).start();
System.out.println(task1.get());
System.out.println(task2.get());
}
}
/*
输出结果:
helloWorld
helloWorld
helloWorld
helloWorld
helloWorld
helloWorld
已经执行了五次
helloWorld
helloWorld
helloWorld
helloWorld
已经执行了五次
*/
获取线程名称并设置线程名称
package com.sj.thread;
public class Demo1Test {
public static void main(String[] args) {
ThreadDemo1 thread1 = new ThreadDemo1();
ThreadDemo1 thread2 = new ThreadDemo1();
System.out.println("默认线程名称");
System.out.println(thread1.getName());
System.out.println(thread2.getName());
System.out.println("修改后的线程名称");
thread1.setName("myThread1");
thread2.setName("myThread2");
System.out.println(thread1.getName());
System.out.println(thread2.getName());
//获取当前正在执行的线程名称
String name = Thread.currentThread().getName();
System.out.println(name);
}
}
/*
输出结果:
默认线程名称
Thread-0
Thread-1
修改后的线程名称
myThread1
myThread2
main
*/
线程优先级,可以给线程设置优先级,优先级高的能获取更多的cpu执行权,优先级参数是1—10之间的整数。线程的优先级高,并不代表就一定先执行,最终还是看cpu的调度。
package com.sj.thread;
public class Demo1Test {
public static void main(String[] args) {
ThreadDemo1 thread1 = new ThreadDemo1();
ThreadDemo1 thread2 = new ThreadDemo1();
//获取当前线程的默认优先级
System.out.println("获取当前线程的默认优先级");
int priority1 = thread1.getPriority();
int priority2 = thread2.getPriority();
System.out.println("thread1"+":"+priority1);
System.out.println("thread2"+":"+priority2);
//线程优先级,取值范围
System.out.println("线程优先级只能是1—10之间的整数");
thread1.setPriority(Thread.MIN_PRIORITY);
thread2.setPriority(Thread.MAX_PRIORITY);
int priority5 = thread1.getPriority();
int priority6 = thread2.getPriority();
System.out.println("thread1"+":"+priority5);
System.out.println("thread2"+":"+priority6);
//设置线程优先级
System.out.println("设置线程优先级");
thread1.setPriority(1);
thread2.setPriority(9);
int priority3 = thread1.getPriority();
int priority4 = thread2.getPriority();
System.out.println("thread1"+":"+priority3);
System.out.println("thread2"+":"+priority4);
}
}
/*
输出结果:
获取当前线程的默认优先级
thread1:5
thread2:5
线程优先级是1—10之间的整数
thread1:1
thread2:10
设置线程优先级
thread1:1
thread2:9
*/
线程的生命周期
线程的生命周期包含5个阶段,包括:新建、就绪、运行、阻塞、销毁。
- 新建:就是刚使用new方法,new出来的线程;
- 就绪:就是调用的线程的start()方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行;
- 运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能;
- 阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep()、wait()之后线程就处于了阻塞状态,这个时候需要其他机制将处于阻塞状态的线程唤醒,比如调用notify或者notifyAll()方法。唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态;
- 销毁:如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源;
线程控制
改变线程的状态,主要有几个方法
1.sleep 方法,是Thread类的静态方法,调用该方法,会使当前线程进入阻塞状态,释放cpu资源,但不释放锁资源,当设定的休眠时间到的时候,线程会重新进入就绪状态,和其他的线程进行争夺cpu的执行权。
2.wait 方法,是Object类中的方法,只能在同步方法或者同步代码块中使用,主要用于线程之间的通信,调用该方法后,线程进入阻塞状态,释放cpu和锁资源,当调用notify或者notifyAll方法时,线程进入就绪状态,和其他线程争抢cpu执行权。
3.yield方法,是Thread类中的静态方法,调用该方法会使当前正在运行的线程进入到就绪状态,重新和其他线程进行cpu资源的争夺。
4.join 方法,当一个线程调用该方法时,该线程会占据cpu的执行权,直到任务执行完,然后释放cpu。
5.deamon方法,线程调用该方法,表示把该线程设置为守护线程,在主线程执行完后,jvm会直接退出,不管该线程执行进度。