1. 线程
- 定义多线程类:
- 继承Thread类,并重写run方法。
- 实现Runnable接口,并重写run方法。【推荐使用】
注意:
- Thread类实现了Runnable接口。
- 由于Java是单继承机制,若要变为多线程的类已经继承了其他类,那么其只能使用第二种方法,即继承Runnable接口。
- 主线程与子线程:CPU调度的最小单位为线程,故一个进程至少包含一个线程。运行java程序形成的进程也至少包含一个线程,这个线程又叫
主线程
。主线程执行的内容为public static void main(String[] args) { }
中的内容。
- 创建子线程语法:
- 若采用继承Thread类,则:
- 若采用实现Runnable接口,则:
注意:不能直接调run方法,因为run方法没有开启线程的功能。
- 主线程不会阻塞,子线程会阻塞。
- 同一线程对象开启多个线程:
- 好处:由同一个线程对象所开启的线程不仅共享
静态属性
,还共享其普通属性
。
- 语法:
- 一个售票系统的例子:
- 线程结束
- 子线程中无while循环,则不可随时结束此线程,只能执行完后自然结束。
- 子线程中有while循环,则可以随时结束此线程。实现步骤:
① 在多线程类中定义一个boolean类型的属性。
② 将该属性作为循环条件。
③ 主线程采用通知方式修改该属性值。
- 用户线程 与 守护线程
- 用户线程 / 工作线程:当线程任务执行完(无while)或采用通知方式结束(有while),线程才会结束。
- 守护线程:只有当所有的用户线程结束后,守护线程才结束。如:垃圾回收机制。
- 默认情况下,一个线程的结束不会影响其他线程的结束,且当所有线程结束后,进程才结束。
- 若要实现功能:主线程结束,则所有子线程结束。则可以将所有子线程设置为守护线程。
语法为:
- 线程编程规律:
- 若有几个功能需要并发的执行,则必须使用线程。因为进程是一行一行的代码执行,不能实现并发。
- 多个功能不相同的线程,则一个类编写一个功能。
多个功能相同的线程,则只需编写一个类,使用一个实例化线程对象开启多个线程。
练习:
分析:
代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws InterruptedException {
AA aa = new AA();
BB bb = new BB(aa);
new Thread(aa).start();
new Thread(bb).start();
}
}
class AA implements Runnable {
private boolean isLoop = true; // 以便其他线程采用通知方式关闭此线程
@Override
public void run() {
while (isLoop) {
System.out.println((int) (Math.random() * 100));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public void setLoop(boolean loop) {
isLoop = loop;
}
}
class BB implements Runnable {
private AA aa;
private Scanner input = new Scanner(System.in);
public BB(AA aa) {
this.aa = aa;
}
@Override
public void run() {
while (true) {
char c = input.next().charAt(0);
if ('Q' == c) {
aa.setLoop(false); // 通知AA线程结束
break;
}
}
}
}
2. Thread类
3. 线程七大状态
4. 线程同步
- 线程同步:指在多线程中,对于线程的共享数据,保证该数据在任何时刻最多只有一个线程访问,保证数据的完整性。
【即 互斥的请求某种共享资源】- 实现线程同步:
- 非静态:
互斥锁为共享的同一对象
- 同步普通方法:默认互斥锁为:当前对象this
- 同步代码块,且该语句在普通方法内:互斥锁为:传入的对象。
- 静态:
互斥锁同一类.class
- 同步静态方法:默认互斥锁为:所在线程类.class
- 同步代码块,且此语句在静态方法内:互斥锁为:传入的类.class
小结:判断是否实现线程的核心:互斥锁是否为
共享的同一对象
或同一类.class
,即是否是同一把锁
练习:
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws InterruptedException {
// 实例化两个人
Person p1 = new Person("张三");
Person p2 = new Person("李四");
// 两个人并发的不断取钱
new Thread(new Card(p1)).start();
new Thread(new Card(p2)).start();
}
}
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Card implements Runnable {
private static double money = 1000; // 卡的金额
private static boolean isLoop = true; // 用于控制是否循环取钱
private Person person;
public Card(Person person) {
this.person = person;
}
// 因为实例化两个线程对象,而互斥锁必须为共享的对象或类.class。我选择了用static
public static synchronized boolean outMoney(Person person, double oMoney) {
// 是否有钱取
if (money <= 0) {
System.out.println("余额为0,终止取钱");
isLoop = false;
return false;
}
if (money < oMoney) {
System.out.println("余额不足,此次取钱失败!");
return false;
}
// 取钱
money -= oMoney;
System.out.println(person.getName() + "取钱成功!此次取钱" + oMoney + "元,还剩" + money + "元");
return true;
}
@Override
public void run() {
// 不断的取钱
while (isLoop) {
outMoney(person, 100); // 每次取 100元
// 休眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
5. 线程死锁
import java.util.Objects;
public class Main {
public static void main(String[] args) throws InterruptedException {
AA aa1 = new AA(1);
AA aa2 = new AA(2);
new Thread(aa1).start();
new Thread(aa2).start();
}
}
class AA implements Runnable {
private static Object o1 = new Object(); // 第一种资源的锁
private static Object o2 = new Object(); // 第二种资源的锁
private int resourceType = 1; // 1表示第一种资源;2表示第二种资源
public AA(int resourceType) {
this.resourceType = resourceType;
}
@Override
public void run() {
if (1 == resourceType) {
synchronized (o1) { // 互斥的请求第一种资源
System.out.println(Thread.currentThread().getName() + "进入1");
synchronized (o2) { // 互斥的请求第二种资源
System.out.println(Thread.currentThread().getName() + "进入2");
}
}
} else if (2 == resourceType) {
synchronized (o2) { // 互斥的请求第二种资源
System.out.println(Thread.currentThread().getName() + "进入3");
synchronized (o1) { // 互斥的请求第一种资源
System.out.println(Thread.currentThread().getName() + "进入4");
}
}
}
}
}
输出: