多线程
一、多线程的创建
1、方式一:继承于Thread类
(1)创建一个继承于Thread类的子类
(2)重写Thread类的run() —> 将此线程执行的操作声明在run()中
(3)创建Thread类的子类的对象
(4)通过此对象调用start()
例子:遍历100以内的所有的偶数
//1.创建一个继承于Thread类的子类
class MyThread extends Thread{
//2.重写Thread类的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 t1 = new MyThread();
//4.通过此对象调用start()
t1.start(); //(1)启动当前线程 (2)调用当前线程的run()
/*
* 问题一:我们不能通过直接调用run()的方式启动线程
t1.run();
* 问题二:再启动一个线程,遍历100以内的偶数。不可以让已经start()的线程
去执行。会报IllegalThreadStateException异常
t1.start();
*/
//我们需要重新创建一个线程的对象
MyThread t2 = new MyThread();
t2.start();
/*start函数之前的都是主线程做的事
* 下面的打印操作和主线程调用的子线程是同时执行的,所以执行顺序没有谁先谁后
* 执行结果和自己的CPU频率有关
*/
//如下操作仍然是在main()线程中执行的。
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ":" +
i + "*****main()*******");
}
}
}
2、创建多线程的方式二:实现Runnable接口
(1)创建一个实现了Runnable接口的类
(2)创建类去实现Runnable中的抽象方法:run()
(3)创建实现类的对象
(4)将此对象作为参数传递到Thread()类的构造器中,创建Thread类的对象
(5)通过Thread类的对象调用start()
比较创建线程的两种方式:
- 开发中:优先选择:实现Runnable接口的方式
- 原因:
1.实现的方式没有类的单继承性的缺陷型
2.实现的方式更适合来处理多个线程有共享数据的情况。 - 联系:public class Thread implements Runnable
- 相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
//1.创建一个实现了Runnable接口的类
class MThread implements Runnable{
//2.创建类去实现Runnable中的抽象方法: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 ThreadTest1 {
public static void main(String[] args) {
//3.创建实现类的对象
MThread m = new MThread();
//4.将此对象作为参数传递到Thread()类的构造器中,创建Thread类的对象
Thread t1 = new Thread(m);
//5.通过Thread类的对象调用start():
//(1) 启动线程 (2)调用当前线程的run()---> 调用了Runnable类型的target的run()
t1.setName("线程1");
t1.start();
//再启动一个线程,遍历100以内的偶数
Thread t2 = new Thread(m);
t2.setName("线程2");
t2.start();
}
}
二、Thread类中的常用方法
1、start():启动当前线程;调用当前线程的run()
2、run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
3、currentThread():静态方法,返回执行当前代码的线程
4、gerName():获取当前线程的名字
5、setName():设置当前线程的名字
6、yield():释放当前CPU的执行权
7、join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态
8、stop():已过时。当执行此方法时,强制结束当前线程。
9、sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内当前线程是阻塞状态
10、isAlive(): 判断当前线程是否存活
11、线程的优先级:
(1)MAX_PRIORITY: 10
(2) MIN _PRIORITY: 1
(3) NORM_PRIORITY: 5 —> 默认优先级
12、如何获取和设置线程的优先级:
- getPriority(),获取线程的优先级
- setPriority(),设置线程的优先级
说明:高优先级的线程要抢占低优先级cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。
public class ThreadMethodTest {
public static void main(String[] args) {
HelloThread h1 = new HelloThread("Thread:1");
//h1.setName("线程一");
//设置分线程的优先级
h1.setPriority(10);
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()
+ ":" + Thread.currentThread().getPriority() + ":" + i);
}
if(i == 20){
try {
h1.join(); //如果分线程没有被join,但其自身有睡眠阻塞的话,切换到主线程执行的频率大大加大
//有join的话即使分线程有sleep,也是先执行完分线程再执行主线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//System.out.println(h1.isAlive()); //false
}
System.out.println(h1.isAlive()); //false
}
}
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 {
//不能用throws的方式,因为父类Thread中的run方没有throws异常
sleep(1); //1000ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(i % 2 == 0){
System.out.println(Thread.currentThread().getName()
+ ":" + Thread.currentThread().getPriority() + ":" + i);
//这里可以省略Thread.currentThread()
}
// if(i % 20 == 0){
// yield(); //与this.yield()和Thread.currentThread().yield()等价