多线程
一.什么是多线程?
进程:进程是系统进行资源分配和调度的一个独立单位。进程由程序、数据和进程控制块三部分组成。
线程:线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
二.线程的创建
(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 {
}
运行结果同上