多线程

发布时间 2023-09-14 11:58:26作者: Monlogue

多线程

一.什么是多线程?

进程:进程是系统进行资源分配和调度的一个独立单位。进程由程序数据进程控制块三部分组成。

线程:线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。

二.线程的创建

(1)Thread类实现多线程
public class Test01 {

	public static void main(String[] args) {
		Thread thread = Thread.currentThread();
		thread.setName("主线程");
		System.out.println(thread.getName());
	}
}

class Thread01 extends Thread {

	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println("Hello" + Thread.currentThread().getName());
		}

	}

}

public class Test02 {

	public static void main(String[] args) {
		new Thread01().start();
		new Thread01().start();
		new Thread01().start();
		new Thread01().start();
		new Thread01().start();
		System.out.println("World!" + Thread.currentThread().getName());

	}

}
(2)Runnable接口实现多线程
class MyRunnable implements Runnable {

	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println((i + 1) + "Hello" + Thread.currentThread().getName());
		}

	}

}

public class Test03 {

	public static void main(String[] args) {
		MyRunnable myRunnable = new MyRunnable();
		for (int i = 1; i <= 5; i++) {
			new Thread(myRunnable, "线程" + i).start();
			System.out.println("World!" + Thread.currentThread().getName());
		}
	}

}
(3)Callable接口实现多线程
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class MyCallable implements Callable<Integer> {

	@Override
	public Integer call() throws Exception {

		return 999;
	}

}

public class Test04 {

	public static void main(String[] args) {
		try {
			MyCallable myCallable = new MyCallable();
			FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
			new Thread(futureTask, "call方法").start();
			System.out.println(futureTask.get());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

三.线程的状态

1.新建状态

2.就绪状态

3.运行状态

4.阻塞状态

5.死亡状态

四.yield()、sleep()、join()

1、yield()(线程让步)

  yield()是Thread类的静态方法。它能让当前线程暂停,但不会阻塞该线程,而是由“运行状态”进入到“就绪状态”,从而让 其它具有相同优先级的等待线程获取执行权;但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权;也有可能是 当前线程又进入到“运行状态”继续运行!

  值得注意的是,yield()方法不会释放锁

public class Yield implements Runnable {

	@Override
	public void run() {
		for (int i = 1; i <= 10; i++) {
			System.out.println(Thread.currentThread().getName() + i);
			if (i == 6) {
				Thread.yield();
			}
		}
	}

	public static void main(String[] args) {
		Runnable runnable = new Yield();
		new Thread(runnable, "A").start();
		new Thread(runnable, "B").start();
	}
}
B1
B2
B3
B4
B5
B6
A1
A2
A3
A4
A5
A6
A7
A8
A9
A10
B7
B8
B9
B10
2、sleep()(线程休眠)

  sleep()是Thread类的静态方法。该方法声明抛出了InterrupedException异常。所以使用时,要么捕捉,要么声明抛出。

  有2种重载方式:

——static void sleep(long millis):让当前正在执行的线程暂停millis毫秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度和准度的影响。

——static void sleep(long millis , int nanos)让当前正在执行的线程暂停millis毫秒加nanos微秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度和准度的影响。

  sleep() 的作用是让当前线程休眠,即当前线程会从“运行状态“进入到“休眠(阻塞)状态”。sleep()会指定休眠时间,线程休眠的时间会大于/等于该休眠时间;在线程重新被唤醒时,它会由“阻塞状态”变成“就绪状态”,从而等待cpu的调度执行。常用来暂停程序的运行。  

  同时注意,sleep()方法不会释放锁

3、join()(线程插入)

  join() 是Thread的一个实例方法。表示,当某个程序执行流中调用其他线程的join方法时,调用线程将被阻塞,直到被join的线程执行完毕。

有3种重载的形式:

——join():等待被join的线程执行完成

——join(long millis)等待被join的线程的时间最长为millis毫秒,若在millis毫秒内,被join的线程还未执行结束,则不等待。

——join(long millis , int nanos):等待被join的线程的时间最长为millis毫秒加nanos微秒,若在此时间内,被join的线程还未执行结束,则不等待。

即当前线程内,用某个线程对象调用join()后,会使当前线程等待,直到该线程对象的线程运行完毕,原线程才会继续运行。

class TestJoin implements Runnable{

	@Override
	public void run() {
		for (int i = 1; i <=10; i++) {
			System.out.println(Thread.currentThread().getName()+i);
		}
	}
}

public class Join {

	public static void main(String[] args) {
		Thread thread=new Thread(new TestJoin(),"线程01");
		thread.start();
		for (int i = 1; i <=10; i++) {
			System.out.println(Thread.currentThread().getName()+i);
			if (i==8) {
				try {
					thread.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
main1
main2
main3
线程011
线程012
线程013
线程014
线程015
线程016
线程017
线程018
线程019
线程0110
main4
main5
main6
main7
main8
main9
main10

六、线程优先级

java中的线程优先级的范围是1~10,默认的优先级是5。每个线程默认的优先级都与创建它的父线程具有相同的优先级。默认情况下,mian线程具有普通优先级。“高优先级线程”会优先于“低优先级线程”执行。Thread提供了setPriority(int newPriority)和getPriority()方法来设置和返回线程优先级。

  Thread类有3个静态常量:

——MAX_PRIORITY = 10

——MIN_PRIORITY = 1

——NORM_PRIORITY = 5

public class Test01 implements Runnable {

	@Override
	public void run() {
		for (int i = 1; i <= 10; i++) {
			System.out.println(Thread.currentThread().getName() + "正在输出:" + i);
		}
	}

	public static void main(String[] args) {
		Runnable test01 = new Test01();
		Thread thread1 = new Thread(test01, "---");
		Thread thread2 = new Thread(test01, "***");
		thread1.setPriority(Thread.MAX_PRIORITY);
		thread2.setPriority(Thread.MIN_PRIORITY);
		thread1.start();
		thread2.start();
	}
}
***正在输出:1
***正在输出:2
---正在输出:1
***正在输出:3
---正在输出:2
***正在输出:4
***正在输出:5
***正在输出:6
---正在输出:3
---正在输出:4
---正在输出:5
---正在输出:6
---正在输出:7
---正在输出:8
---正在输出:9
---正在输出:10
***正在输出:7
***正在输出:8
***正在输出:9
***正在输出:10

七、Synchronized关键字

【同步代码块】

语法格式:

synchronized(同步对象){

//需要同步的代码

}

但是一般都把当前对象this作为同步对象。比如对于上面的买票的问题,如下:

class Piao implements Runnable {
	private int num = 10;
	private int count;

	@Override
	public void run() {
		while (true) {
			System.out.println(Thread.currentThread().getName() + "进入抢票环节");
			synchronized (this) {
				if (num < 1) {
					System.out.println(Thread.currentThread().getName() + "发现没票了");
					break;
				}
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

				num--;
				count++;
				(((Ticket) Thread.currentThread()).count)++;
				System.out.println(Thread.currentThread().getName() + "抢到了第" + count + "张票,还剩" + num + "张票,共抢到了"
						+ ((Ticket) Thread.currentThread()).count + "张票");
				if (Thread.currentThread().getName().equals("黄牛")) {
					return;
				}
			}
		}
	}
}

public class Ticket extends Thread {
	int count;

	public Ticket(Runnable runnable, String name) {
		super(runnable, name);
	}

	public static void main(String[] args) {
		Runnable runnable = new Piao();
		new Ticket(runnable, "淘票票").start();
		new Ticket(runnable, "黄牛").start();
		new Ticket(runnable, "代理").start();
	}

}
淘票票进入抢票环节
黄牛进入抢票环节
代理进入抢票环节
淘票票抢到了第1张票,还剩9张票,共抢到了1张票
淘票票进入抢票环节
代理抢到了第2张票,还剩8张票,共抢到了1张票
代理进入抢票环节
黄牛抢到了第3张票,还剩7张票,共抢到了1张票
代理抢到了第4张票,还剩6张票,共抢到了2张票
代理进入抢票环节
淘票票抢到了第5张票,还剩5张票,共抢到了2张票
淘票票进入抢票环节
代理抢到了第6张票,还剩4张票,共抢到了3张票
代理进入抢票环节
淘票票抢到了第7张票,还剩3张票,共抢到了3张票
淘票票进入抢票环节
代理抢到了第8张票,还剩2张票,共抢到了4张票
代理进入抢票环节
淘票票抢到了第9张票,还剩1张票,共抢到了4张票
淘票票进入抢票环节
代理抢到了第10张票,还剩0张票,共抢到了5张票
代理进入抢票环节
淘票票发现没票了
代理发现没票了
【同步方法】

也可以采用同步方法。

语法格式为synchronized 方法返回类型方法名(参数列表){

​ // 其他代码

}

class Piao02 implements Runnable {
	private int num = 10;
	private int count;

	@Override
	public void run() {
		while (true) {
			System.out.println(Thread.currentThread().getName() + "进入抢票环节");

			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			try {
				buyTicket();
			} catch (NoPiao02Exception e) {
				break;
			}

		}
	}

	private synchronized void buyTicket() {
		if (num < 1) {
			System.out.println(Thread.currentThread().getName() + "发现没票了");
			throw new NoPiao02Exception();
		}
		System.out.println(Thread.currentThread().getName() + "开始抢票");
		try {
			Thread.sleep(50);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		num--;
		count++;
		(((Ticket02) Thread.currentThread()).count)++;
		System.out.println(Thread.currentThread().getName() + "抢到了第" + count + "张票,还剩" + num + "张票,共抢到了"
				+ ((Ticket02) Thread.currentThread()).count + "张票");
	}
}

public class Ticket02 extends Thread {
	int count;

	public Ticket02(Runnable runnable, String name) {
		super(runnable, name);
	}

	public static void main(String[] args) {
		Runnable runnable = new Piao02();
		new Ticket02(runnable, "淘票票").start();
		new Ticket02(runnable, "黄牛").start();
		new Ticket02(runnable, "代理").start();
	}

}

public class NoPiao02Exception extends RuntimeException {

}

运行结果同上