Java多线程

进程、线程、多线程

一个进程可以包括多个线程,但是至少需要包含一个线程

线程是CPU执行的最小单位

对于单核CPU而言,一次只能执行一个线程

但是由于不同线程间切换速度极快,近似达到了同步执行的效果

线程创建

创建Java的子线程有两种方法

1--继承Thread类

2--实现Runnable接口

3--实现callable接口(了解即可)

建议使用方法2,自定义类实现Runnable接口后便于被多个线程使用

继承Thread类

STEP1:自定义类,继承Thread

STEP2:重写run方法,真正需要执行的程序放在run方法里

STEP3:创建STEP1中的自定义类,调用start方法,运行线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Main{
public static void main(String[] args) {
new MyThread().start(); //创建子线程
//main线程输出
for(int i = 0 ; i < 20 ; i ++){
System.out.println("In main process:"+i);
}
}
}

class MyThread extends Thread{ //自定义MyThread类
@Override
public void run(){//重写父类run方法
for(int i = 0 ; i < 20 ; i ++){
System.out.println("In Thread processs:" + i);
}
}
}
//输出如下,由于两个线程的优先级相同,可以观察到CPU在不同线程间切换
//In Thread processs:0
//...
//In main process:0
//...
//In Thread processs:6
//...

实现Runnable接口

STEP1:自定义类,实现runnable接口

STEP2:实现接口中的run方法,真正需要执行的程序放在run方法里

STEP3:使用STEP1中的自定义类来实例化Thread类,调用start方法,运行线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Main{
public static void main(String[] args) {
//子线程创建
new Thread(new MyRunnable()).start();
//主线程
for(int i = 0 ; i < 20 ; i ++){
System.out.println("In main process"+i);
}
}
}

class MyRunnable implements Runnable{//自定义MyRunable类
public void run(){ //实现run方法
for(int i = 0 ; i < 20 ; i ++){
System.out.println("In Thread processs:" + i);
}
}
}

实现Callable接口

多线程08:实现Callable接口_哔哩哔哩_bilibili

线程停止

JDK中提供的stop、destroy方法目前已经被废弃

目前主流的做法是设置一个变量,来控制线程的运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Main{
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
new Thread(myRunnable,"The thread").start();
try{
Thread.sleep(3);
}catch(InterruptedException e){
e.printStackTrace();
}
myRunnable.stop();
}
}

class MyRunnable implements Runnable{
private boolean alive = true;
public void run(){
while(alive){ //方法写在while内部
System.out.println(Thread.currentThread().getName() + " is running...");
}
}
public void stop(){ //线程终止函数
alive = false;
}
}

线程休眠

每一个对象都有一把锁,sleep不会释放锁

使用Sleep实现简单的计时器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Main{
public static void main(String[] args) {
new Thread(new Clock(),"ClockCounter").start();
}
}
class Clock implements Runnable{
public void run(){
int cnt = 1;
while(cnt < 10){
try{
Thread.sleep(1000);//间隔一秒
}catch (InterruptedException e){ //当休眠被打断时触发异常
e.printStackTrace();
}
System.out.println("The Clock count => " + cnt);
cnt++;
}
}
}

线程礼让

礼让线程,就是让一个进程从运行状态重新变为就绪状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Main{
public static void main(String[] args) {
new Thread(new MyRunnable(),"a").start();
new Thread(new MyRunnable(),"b").start();
}
}
class MyRunnable implements Runnable{
public void run(){
System.out.println(Thread.currentThread().getName() + "开始执行");
Thread.yield();//线程礼让
System.out.println(Thread.currentThread().getName() + "结束执行");
}
}
//若不加礼让;
//a开始执行
//a结束执行
//b开始执行
//b结束执行

//若礼让:(也可能失败)
//a开始执行
//b开始执行
//a结束执行
//b结束执行

线程强制执行

join方法可以被理解成线程间的“插队”

调用该方法可以使得其他线程进入阻塞状态,优先执行某个线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Main{
public static void main(String[] args) throws InterruptedException{
Thread thread = new Thread(new MyRunnable());
thread.start();//开启子线程
for(int i = 1 ; i <= 100 ; i ++){
System.out.println("Normal => " + i);
if(i == 10){ //vip通道插队
thread.join();
}
}
}
}
class MyRunnable implements Runnable{
public void run(){
for(int i = 1 ; i <= 20 ; i ++){
System.out.println("VIP => "+i);
}
}
}
//Normal => 1
//...
//Normal => 10
//VIP => 1
//...
//VIP => 20
//Normal => 11
//...

线程优先级

线程优先级只能保证高优先级的线程优先被调用

但并不是一个严格保证,低优先级的线程仍有可能被提前调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Main{
public static void main(String[] args) throws InterruptedException{
Thread t1 = new Thread(new MyRunnable(),"t1");
t1.setPriority(1);
Thread t2 = new Thread(new MyRunnable(),"t1");
t2.setPriority(2);
Thread t3 = new Thread(new MyRunnable(),"t1");
t3.setPriority(3);

t1.start();t2.start();t3.start();
}
}
class MyRunnable implements Runnable{
public void run(){
System.out.println(Thread.currentThread().getName() + "is running:" + Thread.currentThread().getPriority());
}
}

守护线程

线程包括守护线程和用户线程

虚拟机运行在所有用户线程结束后即结束,不需要等待守护线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Main{
public static void main(String[] args) throws InterruptedException{
Thread god = new Thread(new God());
god.setDaemon(true);//设置守护进程
Thread you = new Thread(new You());
you.setDaemon(false);
god.start();
you.start();
}
}
class God implements Runnable{
public void run(){
while(true){//自身非终止,等待所有用户进程结束后才终止
System.out.println("God bless you");
}
}
}
class You implements Runnable{
public void run(){
for (int i = 0; i < 36500; i++) {
System.out.println("I am alive today");
}
System.out.println("-----Goodbye world-----");//结束标识
}
}

线程同步

例1:售票机卖票问题

三个售票机同时售票,两个售票机可能卖出同一张票,

原因是:因为一个售票机卖出票x时,另一个售票机不会等待其操作结束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Main{
public static void main(String[] args) {
//模拟三个售票机同时卖票
new Thread(new SellTicket(),"SellMachine_1").start();
new Thread(new SellTicket(),"SellMachine_2").start();
new Thread(new SellTicket(),"SellMachine_3").start();
}
}
class SellTicket implements Runnable{
static int ticketNum = 1000;
public void run(){
while (ticketNum >= 1){//模拟卖票过程
try{ //每次卖票,花费10ms(模拟延时,放大问题的发生性)
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
//由于没有加锁,导致可能卖出相同的票,或者出现负数
System.out.println(Thread.currentThread().getName() + " has selled ticket_" + (ticketNum--));
}
}
}
//...
//SellMachine_3 has selled ticket_18
//...
//SellMachine_2 has selled ticket_18
//...
//SellMachine_1 has selled ticket_16
//...

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Main{
public static void main(String[] args) {
SellTicket sellTicket = new SellTicket();//synchronized锁的是同一个实例,而不是一个类
//模拟三个售票机同时卖票
new Thread(sellTicket,"SellMachine_1").start();
new Thread(sellTicket,"SellMachine_2").start();
new Thread(sellTicket,"SellMachine_3").start();
}
}
class SellTicket implements Runnable{
static int ticketNum = 1000;
private boolean flag = true;
public void run(){ //不能直接锁run函数,否则一直等待同一台机器购票完毕
//显示一个机器开始卖票
System.out.println(Thread.currentThread().getName() + " start");
while (flag){//模拟卖票过程
buy();
}
}
//直接对函数施加锁,此时锁的是this指向的实例
//加了锁以后,票依次卖出,哪个机器先买到票,取决于cpu如何分配
public synchronized void buy(){
if(ticketNum >= 1){
System.out.println(Thread.currentThread().getName() + " has selled ticket_" + (ticketNum--));
}else{
flag = false;//终止程序
}
}
}

例2:银行取钱问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class Main {
public static void main(String[] args) {
//新建银行账户
Account myBankAccount = new Account(100,"Peter's account");//创建账户
new Drawing(myBankAccount,50,"Peter").start();//Peter取钱
new Drawing(myBankAccount,100,"girlfriend").start();//girlfriend取钱
}
}

class Account{
private int money;//账户余额
private String name;//账户名

public Account(int money_,String name_){
this.money = money_;
this.name = name_;
}

public int getMoney(){
return money;
}
public void setMoney(int nowMoney){
money = nowMoney;
}
public String getName(){
return name;
}
}

class Drawing extends Thread{
Account account;//需要取钱的账户
int drawingMoney;//需要取的钱
String name;//取钱的名称

public Drawing(Account account,int drawingMoney,String name){
this.account = account;
this.drawingMoney = drawingMoney;
this.name = name;
}

@Override
public void run() {//由于没有加锁,两次操作同时进行,导致余额变为负数

if(account.getMoney() < drawingMoney){
System.out.println("操作人 => " + name);
System.out.println("错误 => 余额不足");
System.out.println("-------------");
return;
}
account.setMoney(account.getMoney() - drawingMoney);
System.out.println("操作人 => " + name);
System.out.println("已成功取出 => " + drawingMoney);
System.out.println("账户余额 => " + account.getMoney());
System.out.println("-------------");
}
}

解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Drawing extends Thread{
Account account;//需要取钱的账户
int drawingMoney;//需要取的钱
String name;//取钱的名称

public Drawing(Account account,int drawingMoney,String name){
this.account = account;
this.drawingMoney = drawingMoney;
this.name = name;
}

@Override
public void run() {
//此处使用synchronized锁住account,不能同时对同一个账户操作
synchronized (account){ //不能直接锁run函数,否则锁的是Drawing实例,无效

if(account.getMoney() < drawingMoney){
System.out.println("操作人 => " + name);
System.out.println("错误 => 余额不足");
System.out.println("-------------");
return;
}
account.setMoney(account.getMoney() - drawingMoney);
System.out.println("操作人 => " + name);
System.out.println("已成功取出 => " + drawingMoney);
System.out.println("账户余额 => " + account.getMoney());
System.out.println("-------------");
}
}
}

Java多线程
https://czwcugb.github.io/其他/Java多线程/
作者
ChenZhiwei
发布于
2025年1月14日
许可协议