一 Java中线程的实现
(1)通过继承Thread类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package
javase.thread;
class
MyThread
extends
Thread{
public
void
run(){
for
(
int
i=
0
;i<
10
;i++)
System.out.println(
"正在运行线程:"
+ currentThread().getName());
}}
public
class
ThreadDemo {
public
static
void
main(String[] args) {
MyThread thread1 =
new
MyThread();
MyThread thread2 =
new
MyThread();
thread1.start();
thread2.start();
}}
|
(2)通过实现Runnable接口
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package
javase.thread;
class
MyRunnable
implements
Runnable{
public
void
run() {
for
(
int
i=
0
;i<
10
;i++)
System.out.println(
"正在运行线程: "
+ Thread.currentThread().getName());
}
}
public
class
RunnableDemo {
public
static
void
main(String[] args) {
MyRunnable runnable1 =
new
MyRunnable();
MyRunnable runnable2 =
new
MyRunnable();
new
Thread(runnable1).start();
new
Thread(runnable2).start();
}}
|
二 线程操作的一些方法
(1)线程的强制启动
在线程操作中,可以使用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
|
package
javase.thread;
class
MyThread2
implements
Runnable{
public
void
run(){
for
(
int
i=
0
;i<
6
;i++)
System.out.println(
"正在运行线程:"
+ Thread.currentThread().getName() +
" --> "
+ i);
}}
public
class
ThreadJoinDemo {
public
static
void
main(String[] args) {
MyThread2 demo =
new
MyThread2();
Thread thread =
new
Thread(demo,
"线程"
);
thread.start();
for
(
int
i=
0
;i<
6
;i++){
System.out.println(
"Main线程运行 --> "
+ i);
if
(i >
2
){
try
{
/**
* 线程强制运行
* 线程强制运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行
* */
thread.join();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
|
输出:
1
2
3
4
5
6
7
8
9
10
11
12
|
Main线程运行 -->
0
正在运行线程:线程 -->
0
正在运行线程:线程 -->
1
正在运行线程:线程 -->
2
正在运行线程:线程 -->
3
正在运行线程:线程 -->
4
正在运行线程:线程 -->
5
Main线程运行 -->
1
Main线程运行 -->
2
Main线程运行 -->
3
Main线程运行 -->
4
Main线程运行 -->
5
|
注:结果不唯一
(2)中断线程
当一个线程运行时,在其他线程中可以中断该线程的运行状态。使用方法:interrupt()
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
|
package
javase.thread;
class
MyThread3
implements
Runnable{
public
void
run(){
System.out.println(
"1 run方法开始执行"
);
try
{
Thread.sleep(
10000
);
System.out.println(
"2 休眠结束"
);
}
catch
(InterruptedException e) {
System.out.println(
"3 休眠被终止"
);
return
;
}
System.out.println(
"4 run方法正常结束"
);
}
}
public
class
ThreadInterruptDemo {
public
static
void
main(String[] args) {
Thread thread =
new
Thread(
new
MyThread3());
thread.start();
//线程启动
try
{
Thread.sleep(
2000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
//主线程暂停2s之后将其中断
}
}
|
输出:
1
2
|
1
run方法开始执行
3
休眠被终止
|
可以看出,在主线程休眠2s之后就将线程类强制中断了,使其run方法并没有正常运行结束
(3)后台线程
前台线程结束了,后台线程仍然可以继续运行。使用方法:setDaemon(true)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package
javase.thread;
class
MyThread4
implements
Runnable{
public
void
run() {
while
(
true
)
System.out.println(Thread.currentThread().getName() +
"在运行"
);
}
}
public
class
ThreadDaemonDemo {
public
static
void
main(String[] args) {
Thread t =
new
Thread(
new
MyThread4(),
"线程"
);
t.setDaemon(
true
);
//此方法需要放在start方法之前才会生效
t.start();
}
}
|
看起来是一个不会结束的死循环,但是由于有了后台进程的缘故,程序仍然可以正常结束
(4)线程的优先级
在Java中,线程一共有3种优先级,分别是:
定义 | 描述 | 表示的常量 |
public static final int MIN_PRIORITY | 线程可以具有的最低优先级 | 1 |
public static final int NORM_PRIORITY | 分配给线程的默认优先级 | 5 |
public static final int MAX_PRIORITY | 线程可以具有的最高优先级 | 10 |
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
|
package
javase.thread;
class
MyThread5
implements
Runnable{
public
void
run(){
for
(
int
i=
0
;i<
3
;i++){
try
{
Thread.sleep(
500
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"正在运行线程:"
+ Thread.currentThread().getName() +
" --> "
+ i);
}
}
}
public
class
ThreadPriorityDemo {
public
static
void
main(String[] args) {
System.out.println(
"主方法的线程优先级: "
+ Thread.currentThread().getPriority());
Thread t1 =
new
Thread(
new
MyThread5(),
"线程1"
);
Thread t2 =
new
Thread(
new
MyThread5(),
"线程2"
);
Thread t3 =
new
Thread(
new
MyThread5(),
"线程3"
);
t1.setPriority(Thread.MIN_PRIORITY);
// 1
t2.setPriority(Thread.NORM_PRIORITY);
// 5
t3.setPriority(Thread.MAX_PRIORITY);
// 10
t1.start();
t2.start();
t3.start();
}
}
|
输出:
1
2
3
4
5
6
7
8
9
10
|
主方法的线程优先级:
5
正在运行线程:线程
1
-->
0
正在运行线程:线程
2
-->
0
正在运行线程:线程
3
-->
0
正在运行线程:线程
1
-->
1
正在运行线程:线程
2
-->
1
正在运行线程:线程
3
-->
1
正在运行线程:线程
2
-->
2
正在运行线程:线程
1
-->
2
正在运行线程:线程
3
-->
2
|
PS:从上面的示例可以看出,主线程的优先级是5,也就是默认的优先级。题外话:Java程序每次运行至少需要启动几个线程?答案是2,一个是main(主)线程,另一个是垃圾回收线程
(5)线程的礼让
在线程操作中,可以使用yield()方法让一个线程将执行权暂时让给另一个线程
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
|
package
javase.thread;
class
MyThread6
implements
Runnable{
public
void
run(){
for
(
int
i=
0
;i<
6
;i++){
System.out.println(
"正在运行线程:"
+ Thread.currentThread().getName() +
" --> "
+ i);
if
(i ==
3
){
System.out.print(Thread.currentThread().getName() +
"进行礼让: "
);
Thread.currentThread().yield();
}
}
}
}
public
class
ThreadYieldDemo {
public
static
void
main(String[] args) {
MyThread6 mThread6 =
new
MyThread6();
Thread t1 =
new
Thread(mThread6,
"线程A"
);
Thread t2 =
new
Thread(mThread6,
"线程B"
);
t1.start();
t2.start();
}
}
|
输出:
1
2
3
4
5
6
7
8
9
10
11
12
|
正在运行线程:线程A -->
0
正在运行线程:线程B -->
0
正在运行线程:线程A -->
1
正在运行线程:线程B -->
1
正在运行线程:线程A -->
2
正在运行线程:线程A -->
3
正在运行线程:线程B -->
2
线程A进行礼让: 正在运行线程:线程B -->
3
正在运行线程:线程A -->
4
线程B进行礼让: 正在运行线程:线程A -->
5
正在运行线程:线程B -->
4
正在运行线程:线程B -->
5
|
三 线程的同步与死锁
(1)问题引出:
我这里以多个线程共同卖票来举例说明。通过Runnable接口实现了多线程,然后实例化3个线程对象共同卖10张票:
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
|
package
javase.thread;
class
MyThread7
implements
Runnable{
private
int
ticket =
10
;
//多个线程共享
public
void
run(){
while
(ticket >
0
){
try
{
Thread.sleep(
100
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"卖票,剩余票数: ticket = "
+ --ticket);
}
}
}
public
class
SyncDemo1 {
public
static
void
main(String[] args) {
MyThread7 mThread7 =
new
MyThread7();
Thread t1 =
new
Thread(mThread7,
"售票员A"
);
Thread t2 =
new
Thread(mThread7,
"售票员B"
);
Thread t3 =
new
Thread(mThread7,
"售票员C"
);
t1.start();
t2.start();
t3.start();
}
}
|
输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
卖票,剩余票数: ticket =
9
卖票,剩余票数: ticket =
9
卖票,剩余票数: ticket =
8
卖票,剩余票数: ticket =
7
卖票,剩余票数: ticket =
7
卖票,剩余票数: ticket =
6
卖票,剩余票数: ticket =
4
卖票,剩余票数: ticket =
5
卖票,剩余票数: ticket =
3
卖票,剩余票数: ticket =
2
卖票,剩余票数: ticket =
2
卖票,剩余票数: ticket =
1
卖票,剩余票数: ticket =
0
卖票,剩余票数: ticket =
0
卖票,剩余票数: ticket = -
1
|
可以看出,最后的结果竟然出现了负数。这就是因为在线程的执行过程中由于没有把需要一起执行的命令部分加锁,导致了本来应该一起执行的几条命令在执行了一部分之后被另一个线程抢过了执行权,因而最后的数据出错
(2)通过添加同步代码块的方式解决:
格式:
synchronized (同步对象) {
需要同步的代码
}
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
|
package
javase.thread;
class
MyThread8
implements
Runnable{
private
int
ticket =
10
;
public
void
run(){
//添加同步代码块
synchronized
(
this
) {
while
(ticket >
0
){
try
{
Thread.sleep(
100
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"卖票,剩余票数: ticket = "
+ --ticket);
}
}
}
}
public
class
SyncDemo2 {
public
static
void
main(String[] args) {
MyThread8 mThread8 =
new
MyThread8();
Thread t1 =
new
Thread(mThread8,
"售票员A"
);
Thread t2 =
new
Thread(mThread8,
"售票员B"
);
Thread t3 =
new
Thread(mThread8,
"售票员C"
);
t1.start();
t2.start();
t3.start();
}
}
|
输出:
1
2
3
4
5
6
7
8
9
10
|
卖票,剩余票数: ticket =
9
卖票,剩余票数: ticket =
8
卖票,剩余票数: ticket =
7
卖票,剩余票数: ticket =
6
卖票,剩余票数: ticket =
5
卖票,剩余票数: ticket =
4
卖票,剩余票数: ticket =
3
卖票,剩余票数: ticket =
2
卖票,剩余票数: ticket =
1
卖票,剩余票数: ticket =
0
|
可以看出这下结果就很正确了
(3)通过添加同步方法的方式解决:
格式:
访问权限{public|default|protected|private} synchronized 方法返回值 方法名称(参数列表){
}
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
|
package
javase.thread;
class
MyThread9
implements
Runnable {
private
int
ticket =
10
;
public
void
run() {
sale();
}
/**
* 同步方法
* */
public
synchronized
void
sale() {
while
(ticket >
0
) {
try
{
Thread.sleep(
100
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"卖票,剩余票数: ticket = "
+ --ticket);
}
}
}
public
class
SyncDemo3 {
public
static
void
main(String[] args) {
MyThread9 mThread9 =
new
MyThread9();
Thread t1 =
new
Thread(mThread9,
"售票员A"
);
Thread t2 =
new
Thread(mThread9,
"售票员B"
);
Thread t3 =
new
Thread(mThread9,
"售票员C"
);
t1.start();
t2.start();
t3.start();
}
}
|
输出:略
四 线程操作经典实例——生产者与消费者
要求是:生产者生产出一个产品后消费者才能消费,否则等待,一直到生产者生产出产品后将消费者唤醒;反之亦然。关于等待和唤醒分别是这两个方法:wait()和notify()。代码如下:
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
|
package
javase.thread;
/**
* 表示一个网站的基本信息
* */
class
Info {
private
String name;
private
String url;
private
boolean
flag =
false
;
// 标记赋值状态,false表示还未赋值
/**
* 赋值
* */
public
synchronized
void
setValues(String name, String url) {
// 如果已被赋值,等待
if
(flag) {
try
{
this
.wait();
// 已经赋值而且值未被使用,则一直等待被使用
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
this
.name = name;
try
{
Thread.sleep(
100
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
this
.url = url;
flag =
true
;
// 赋值之后,更新标记状态
this
.notify();
// 赋值之后,唤醒等待取值的线程
}
/**
* 取值
* */
public
synchronized
void
getValues() {
// 如果未被赋值,等待
if
(!flag) {
try
{
this
.wait();
// 上一次赋的值已经使用过,下一次的还未生产
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
try
{
Thread.sleep(
100
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
System.out.println(
"站点:"
+ name +
" --> URL:"
+ url);
flag =
false
;
this
.notify();
// 唤醒等待赋值的线程
}
}
/**
* 生产者,负责给参数赋值
* */
class
Producer
implements
Runnable {
private
Info info =
null
;
public
Producer(Info info) {
this
.info = info;
}
public
void
run() {
for
(
int
i =
0
; i <
10
; i++) {
// 交替赋值
if
(i %
2
==
0
)
info.setValues(
"zifangsky的个人博客"
,
"http://www.zifangsky.cn"
);
else
info.setValues(
"51CTO博客"
,
"http://blog.51cto.com"
);
}
}
}
/**
* 消费者,负责取值
* */
class
Consumer
implements
Runnable {
private
Info info =
null
;
public
Consumer(Info info) {
this
.info = info;
}
public
void
run() {
for
(
int
i =
0
; i <
10
; i++)
info.getValues();
}
}
public
class
Demo {
public
static
void
main(String[] args) {
Info info =
new
Info();
Producer producer =
new
Producer(info);
Consumer consumer =
new
Consumer(info);
new
Thread(producer).start();
new
Thread(consumer).start();
}
}
|
输出:
1
2
3
4
5
6
7
8
9
10
|
站点:zifangsky的个人博客 --> URL:http:
//www.zifangsky.cn
站点:51CTO博客 --> URL:http:
//blog.51cto.com
站点:zifangsky的个人博客 --> URL:http:
//www.zifangsky.cn
站点:51CTO博客 --> URL:http:
//blog.51cto.com
站点:zifangsky的个人博客 --> URL:http:
//www.zifangsky.cn
站点:51CTO博客 --> URL:http:
//blog.51cto.com
站点:zifangsky的个人博客 --> URL:http:
//www.zifangsky.cn
站点:51CTO博客 --> URL:http:
//blog.51cto.com
站点:zifangsky的个人博客 --> URL:http:
//www.zifangsky.cn
站点:51CTO博客 --> URL:http:
//blog.51cto.com
|
附:另一个类似实例,关于生产电脑和卖电脑:
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
|
package
javase.thread;
class
Computer{
private
String name;
private
boolean
status =
false
;
//标记computer状态,false表示还未生产
private
int
sum =
0
;
public
synchronized
String getName() {
if
(!status){
//未生产,等待电脑生产
try
{
this
.wait();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
status =
false
;
notify();
return
name;
}
public
synchronized
void
setName(String name) {
if
(status){
/**
* 如果生产的电脑没有卖出,则要等待电脑卖出之后再生产,
* 并统计出生产的电脑数量
* */
System.out.println(
"到目前为止,一共生产出了 "
+ sum +
" 台电脑"
);
try
{
this
.wait();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
this
.name = name;
sum++;
status =
true
;
notify();
}
}
/**
* 生产电脑
* */
class
ComputerProducer
implements
Runnable{
Computer computer =
null
;
public
ComputerProducer(Computer computer) {
this
.computer = computer;
}
public
void
run() {
for
(
int
i=
0
;i<
10
;i++)
computer.setName(
"zifangsky00"
+ i);
}
}
/**
* 卖电脑
* */
class
ComputerConsumer
implements
Runnable{
Computer computer =
null
;
public
ComputerConsumer(Computer computer) {
this
.computer = computer;
}
public
void
run() {
for
(
int
i=
0
;i<
10
;i++)
System.out.println(
"卖出电脑,编号是: "
+ computer.getName());
}
}
public
class
Demo2 {
public
static
void
main(String[] args) {
Computer computer =
new
Computer();
ComputerProducer producer =
new
ComputerProducer(computer);
ComputerConsumer consumer =
new
ComputerConsumer(computer);
new
Thread(producer).start();
new
Thread(consumer).start();
}
}
|
输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
到目前为止,一共生产出了
1
台电脑
卖出电脑,编号是: zifangsky000
到目前为止,一共生产出了
2
台电脑
卖出电脑,编号是: zifangsky001
到目前为止,一共生产出了
3
台电脑
卖出电脑,编号是: zifangsky002
到目前为止,一共生产出了
4
台电脑
卖出电脑,编号是: zifangsky003
到目前为止,一共生产出了
5
台电脑
卖出电脑,编号是: zifangsky004
到目前为止,一共生产出了
6
台电脑
卖出电脑,编号是: zifangsky005
到目前为止,一共生产出了
7
台电脑
卖出电脑,编号是: zifangsky006
到目前为止,一共生产出了
8
台电脑
卖出电脑,编号是: zifangsky007
到目前为止,一共生产出了
9
台电脑
卖出电脑,编号是: zifangsky008
卖出电脑,编号是: zifangsky009
|