浅谈Java多线程(一)

Java支持多线程,可以通过继续Thread类或实现Runnable接口定义线程类,并在该类中重写run方法。
一个简单的Java线程类MyThread如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.wt.testThread;  

public class MyThread extends Thread{
public void run()
{
int i = 0;
while (true)
{
i++;
System.out.println("MyThread Processing --> "+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {}
}
}

public static void main(String args[])
{
MyThread thread = new MyThread();
thread.start();
}
}

在MyThread的run方法中,每隔500ms(Thread.sleep(500);)输出一次信息。在主函数中,新建MyThread实例对象,并使用对象的start方法启动该线程实例。程序输出结果部分如下所示。

MyThread Processing –> 1
MyThread Processing –> 2
MyThread Processing –> 3

多线程的同步

假设现在有两件工具,三个工人,每个工人工作时需要同时使用这两件工具。使用Java多线程模拟上述情景,使用线程类WorkThread表示工人,使用ToolBox表示两件工具所在的工具箱,如下所示。

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
class ToolBox{  
//t1Owner,t2Owner分别表示正在使用该工具的工人名
private String t1Owner;
private String t2Owner;

//useTool表示某位工人取得工具并使用
public void useTool(String workerName) throws InterruptedException
{
t1Owner = workerName;
Thread.sleep(100);
t2Owner = workerName;
Thread.sleep(100);
checkTool();
}

//useTool检查工具是否正在被同一个工人使用
public void checkTool()
{
if (!t1Owner.equals(t2Owner)){
System.out.println("*Wrong*: Tool1:"+t1Owner+"\t Tool2:"+t2Owner);
}
else{
System.out.println("*Right*: Tool1:"+t1Owner+"\t Tool2:"+t2Owner);
}
}
}

public class WorkerThread extends Thread{
private ToolBox toolBox;
private String workerName;

public WorkerThread(String workerName,ToolBox toolBox)
{
super();
this.workerName = workerName;
this.toolBox = toolBox;
}

public void run()
{
//工人每次休息1秒,然后使用工具工作
while(true)
{
try {
Thread.sleep(1000);
toolBox.useTool(workerName);
} catch (InterruptedException e) {}
}
}
}

主函数如下所示,其中ToolBox类实例toolBox表示工人共用的工具箱,通过WorkerThread类的构造函数构造三个工人线程类,并传入工人姓名和toolBox,启动线程。

1
2
3
4
5
6
7
8
9
public static void main(String args[])
{
//工人共用的工具箱
ToolBox toolBox = new ToolBox();
//三个工人开始工作
new WorkerThread("Tom",toolBox).start();
new WorkerThread("Jack",toolBox).start();
new WorkerThread("Bob",toolBox).start();
}

执行结果部分如下所示。

*Right*: Tool1:Bob Tool2:Bob
*Right*: Tool1:Bob Tool2:Bob
*Wrong*: Tool1:Bob Tool2:Jack
*Wrong*: Tool1:Bob Tool2:Jack
*Wrong*: Tool1:Bob Tool2:Jack
*Right*: Tool1:Jack Tool2:Jack
*Right*: Tool1:Jack Tool2:Jack
*Right*: Tool1:Jack Tool2:Jack
*Wrong*: Tool1:Jack Tool2:Tom
*Wrong*: Tool1:Jack Tool2:Tom
*Wrong*: Tool1:Jack Tool2:Tom
*Right*: Tool1:Tom Tool2:Tom

可以看出,由于三个工人线程类共享使用工具箱中的工具,导致存在对于工具使用的竞争。由于ToolBox中useTool方法的代码(即工人使用工具)存在上述多线程之间的资源竞争,因此将其称为临界区(Critical Section)。临界区如下所示。

1
2
3
4
5
t1Owner = workerName;  
Thread.sleep(100);
t2Owner = workerName;
Thread.sleep(100);
checkTool();

对于上述临界区中的代码,需要实现同步,即在一个线程中,要么都执行,要么都不执行。在Java中,可以使用关键字synchronized实现同步。synchronized的使用方法有两种:

  1. 对于需要同步的共享实例对象,将synchronized添加为方法的修饰符,如下所示。

    1
    public synchronized void useTool(String workerName) throws InterruptedException
  2. 将需要同步的临界区代码放入synchronized(this){}中,如下所示。

    1
    2
    3
    4
    5
    6
    7
    8
    synchronized(this)  
    {
    t1Owner = workerName;
    Thread.sleep(100);
    t2Owner = workerName;
    Thread.sleep(100);
    checkTool();
    }

使用synchronized同步后,重新执行原主函数,得到正确执行结果。

*Right*: Tool1:Tom Tool2:Tom
*Right*: Tool1:Bob Tool2:Bob
*Right*: Tool1:Jack Tool2:Jack
*Right*: Tool1:Tom Tool2:Tom
*Right*: Tool1:Bob Tool2:Bob
*Right*: Tool1:Jack Tool2:Jack
*Right*: Tool1:Tom Tool2:Tom
*Right*: Tool1:Bob Tool2:Bob
*Right*: Tool1:Jack Tool2:Jack
*Right*: Tool1:Tom Tool2:Tom
*Right*: Tool1:Bob Tool2:Bob
*Right*: Tool1:Jack Tool2:Jack
*Right*: Tool1:Tom Tool2:Tom
*Right*: Tool1:Bob Tool2:Bob
*Right*: Tool1:Jack Tool2:Jack
*Right*: Tool1:Tom Tool2:Tom