线程(2)

线程生命周期

线程具有生命周期,当线程被创建并启动后,不会立即进入执行状态,也不会一直处于执行状态。

在线程的生命周期中,要经过5种状态:

• 新建 
• 就绪
• 运行 
• 阻塞
• 死亡

注意点:

• 只能对新建状态的线程调用start方法,且只能调用一次
• 如果调用start()方法后需要线程立即开始执行,可以使用Thread.sleep(1)来让当前运行的主线程休眠1毫秒,此时子线程处于就绪状态的子线程
• 被阻塞的线程阻塞解除后进入就绪状态

相关的一些方法:

  • sleep():暂停执行的方法,参数以毫秒为单位,注意抛出异常
  • 如果一个线程包含了很长的循环,在循环的每次迭代之后把该线程切换到sleep休眠状态时一种很好的策略,这可以保证其他线程不必等待很长时间就能轮到处理器执行。
  • isALive():判断线程是否处于执行状态,当处于就绪,运行,阻塞状态时返回true
  • stop():直接停止线程,容易导致死锁,通常不推荐使用
  • join():可以让一个线程等待另一个线程完成后,继续执行原线程中的任务。当在某个程序执行流中调用其他线程的join方法时,当前线程将会被阻塞,直到另一个线程执行完成为止。

关于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
28
29
30
31
32
33
34
35
36
package 线程;

class JoinThread extends Thread {
public JoinThread(String name) {
// TODO Auto-generated constructor stub
super(name);
}

@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i <= 10; i++)
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}

public class JoinDemo {
public static void main(String[] args) {
// 输出当前线程的名称,发现为main
System.out.println(Thread.currentThread().getName());
JoinThread jThread = new JoinThread("子线程");
jThread.start();
// 输出当前线程的名称,发现依然为main jThread只是就绪
System.out.println(Thread.currentThread().getName());
try {
jThread.join();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
// 输出当前线程的名称,发现为main
System.out.println(Thread.currentThread().getName());
// 输出当前子线程的状态
System.out.println(jThread.isAlive());
}
}

结果如下:

JoinDemo运行结果

线程优先级

  • 每个线程执行时都有一定的优先级,系统根据优先级调度线程
  • 每个线程都有默认的优先级,与父线程相同
  • 主线程具有普通优先级
  • Thread类提供三个静态常量来标识线程的优先级
    • MAX_PRIORITY 最高优先级,其值为10
    • NORM_PRIORITY 普通优先级,其值为5,默认优先级
    • MIN_PRIORITY 最低优先级,值为1
  • 优先级高的线程提前获得执行的机会也会更多

线程同步

多线程访问同一资源会带来安全问题,java中提供了线程同步的概念来保证某个资源在
某一时刻只能由一个线程访问,保证共享数据的一致性
线程同步通常采用以下三种方式:

  • 同步代码块
  • 同步方法
  • 同步锁

同步代码块:

将对实例的访问语句放入一个同步块中,语法格式如下:

1
2
3
synchronized(object){
//需要同步的代码块
}

其中synchronized是关键字,object是监视器
实例,客户在银行中存钱,账户唯一,用线程进行存取钱操作,初始余额5000.

假设客户进行了5次操作,如果没有同步,可能执行时候每个操作都以初始值5000为起点,最后得到的值是错的;
而执行同步操作后,每次只进行一种操作,以上次操作结果为起点,可以保证操作的正确性

同步方法:

使用synchronized关键字修饰需要同步的方法,语法格式如下:

1
2
3
[访问修饰符]synchronized 返回类型 方法名(参数){
//方法体
}

一个具有同步方法的类也被称为“线程安全类”

synchronized锁定的是对象,而不是代码块或方法,也可以修饰类

同步锁:

Lock是控制多个线程对共享资源进行访问的工具,能够对共享资源进行独占访问

有ReentrantLock(可重入锁)实现类
步骤:

  1. 定义一个ReentrantLock锁对象,该对象是final常量
  2. 在需要保证线程安全的代码之前增加“加锁”操作
  3. 在执行完线程安全的代码后释放锁

代码示例(使用同步方法,同步锁):

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package 线程;

import java.util.concurrent.locks.ReentrantLock;

//银行账户
public class BankAccount {
// 银行账户
private String bankNo;
// 银行余额
private double balance;
//定义锁对象
private final ReentrantLock lock=new ReentrantLock();

public BankAccount(String bankNO, double balance) {
// TODO Auto-generated constructor stub
this.bankNo = bankNO;
this.balance = balance;
}

public double getBalance() {
return balance;
}

public String getBankNo() {
return bankNo;
}

public void setBalance(double balance) {
this.balance = balance;
}

public void setBankNo(String bankNo) {
this.bankNo = bankNo;
}
public synchronized void access(double money) {
// TODO Auto-generated method stub
//lock.lock();
try {
if(money<0&balance<-money)
{
System.out.println("余额不足,操作失败");
return;
}
else {
balance+=money;
System.out.println(Thread.currentThread().getName()+"操作成功,目前余额为:"+balance);
try {
Thread.sleep(1);
}catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally {
//lock.unlock();
}
}

}


package 线程;

public class BankLock extends Thread {
private BankAccount account;
private double money;

public BankLock(String name, BankAccount account, double money) {
// TODO Auto-generated constructor stub
super(name);
this.money = money;
this.account = account;
}

// 线程任务
@Override
public void run() {

this.account.access(money);
}

public static void main(String[] args) {
// 创建账户,启动线程
BankAccount cAccount = new BankAccount("WT", 5000);
BankLock t1 = new BankLock("task1", cAccount, -3000);
BankLock t2 = new BankLock("task2", cAccount, -3000);
BankLock t3 = new BankLock("task3", cAccount, 1000);
BankLock t4 = new BankLock("task4", cAccount, -2000);
BankLock t5 = new BankLock("task5", cAccount, 2000);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
try {
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
System.out.println("账户:" + cAccount.getBankNo());
System.out.println("余额:" + cAccount.getBalance());
}
}

线程通信

加入目前系统中有生产和消费两个线程,系统要求不断重复生产,消费操作,并要求每当一个线程生产后,另一个线程立即进行消费,不允许连续两次生产,也不允许连续两次消费,实现这种功能,可以采用线程间的通信技术
相关方法:


线程(2)
https://shanhainanhua.github.io/2019/09/06/线程-2/
作者
wantong
发布于
2019年9月6日
许可协议