Java的线程笔记

线程的实现

  1. 继承Thread类并重写run方法。
    1
    2
    3
    4
    5
    6
    class MyThread extends Thread {
    @Override
    public void run() {
    // 处理具体的逻辑
    }
    }

运行

1
2
3
4
MyThread myThread = new MyThread();
myThread.start();
------------------------------------------------------
new MyThread().start();//匿名写法

  1. 通过定义实现Runnable接口的类进而实现 run方法
    1
    2
    3
    4
    5
    6
    class MyThread implements Runnable {
    @Override
    public void run() {
    // 处理具体的逻辑
    }
    }

运行

1
2
3
4
5
6
7
8
9
10
MyThread myThread = new MyThread();
new Thread(myThread).start();
------------------------------------------------------------
//匿名类写法
new Thread(new Runnable() {
@Override
public void run() {
// 处理具体的逻辑
}
}).start();

人为停止线程推荐的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MyThread implements Runnable {
private boolean flag=true
public void run(){
while (flag) {
…}
}
public void stopRunning() {
flag = false;
}
}
public class ControlThread {
private Runnable r=new MyThread();
private Thread t=new Thread(r);
public void startThread() {
t.start();
}
public void stopThread(){
r.stopRunning();}
}

同步锁(synchronized)

成员变量与局部变量

1.如果一个变量是成员变量,那么多个线程对同一个对象是彼此影响的(一个线程对成员的改变会影响另一个线程)。
例子

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 Thread2 {
public static void main(String[] args) {
// TODO 自动生成的方法存根
Runnable r=new HelloThread();
Thread t1=new Thread(r);
Thread t2=new Thread(r);
t1.start();
t2.start();
}
}
class HelloThread implements Runnable {
int i;
@Override
public void run() {
while (true) {
System.out.println("number:"+i++);
try {
Thread.sleep((long) (Math.random()*1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
if(10==i){
break;
}
}
}
}

运行结果

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
public class Thread2 {
public static void main(String[] args) {
// TODO 自动生成的方法存根
Runnable r=new HelloThread();
Thread t1=new Thread(r);
Thread t2=new Thread(r);
t1.start();
t2.start();
}
}
class HelloThread implements Runnable {
@Override
public void run() {
int i=0;
while (true) {
System.out.println("number:"+i++);
try {
Thread.sleep((long) (Math.random()*1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
if(10==i){
break;
}
}
}
}

运行结果

静态方法和非静态方法

Java 中的每个对象都有一个锁(lock)或者叫做监视器(monitor),当访问某个对象的 synchronized 方法时,表示将该对象上锁,此时其他任何线程都无法再去访问 该 synchronized 方法了,直到之前的那个线程执行方法完毕后(或者是抛出了异常)
, 那么将该对象的锁释放掉,其他线程才有可能再去访问该 synchronized 方法。

如果一个对象有多个 synchronized方法,某一时刻某个线程已经进入到了某个 synchronized方法 ,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何 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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class TheThread2 {
public static void main(String[] args) {
Example example=new Example();
Thread t1=new TheThread(example);
Thread t2=new TheThread1(example);
t1.start();
t2.start();
}
}
class Example{
public synchronized void execute(){
for(int i=0;i<20;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello"+i);
}
}
public synchronized void execute2(){
for(int i=0;i<20;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("world"+i);
}
}
}
class TheThread extends Thread{
private Example example;
public TheThread(Example example){
this.example=example;
}
@Override
public void run() {
example.execute();
super.run();
}
}
class TheThread1 extends Thread{
private Example example;
public TheThread1(Example example){
this.example=example;
}
@Override
public void run() {
example.execute2();
super.run();
}
}

运行结果

如果某个 synchronized 方法是 static 的,那么当线程访问该方法时,它锁的并不是 synchronized 方法所在的对象,而是 synchronized 方法所在的对象所对应的 Class 对 象,因为 Java 中无论一个类有多少个对象,这些对象会对应唯一一个 Class 对象

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
public class TheThread2 {
public static void main(String[] args) {
Example example=new Example();
Thread t1=new TheThread(example);
Thread t2=new TheThread1(example);
t1.start();
t2.start();
}
}
class Example{
public synchronized static void execute(){
.....
}
public synchronized void execute2(){
...
}
class TheThread extends Thread{
...
}
class TheThread1 extends Thread{
...
}

运行结果

因此当线程分别访问同一个类的两个对象的两个 static,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
public class TheThread2 {
public static void main(String[] args) {
Example example=new Example();//不管生成多少个新对象也可以成功加锁
Thread t1=new TheThread(example);
Thread t2=new TheThread1(example);
t1.start();
t2.start();
}
}
class Example{
public synchronized static void execute(){
.....
}
public synchronized static void execute2(){
...
}
class TheThread extends Thread{
...
}
class TheThread1 extends Thread{
...
}

运行结果

同步代码块

表示线程在执行的时候会对 object 对象上锁,锁的只是方法的某一个代码块。
同步代码块是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、同步代码块块之外的代码是可以被多个线程同时访问到的。

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
public class TheThread2 {
public static void main(String[] args) {
Example example=new Example();
Thread t1=new TheThread(example);
Thread t2=new TheThread1(example);
t1.start();
t2.start();
}
}
class Example{
private Object o1=new Object();
private String o2="233";
public void execute(){
synchronized (o1) {
.....
}
}
public void execute2(){
synchronized (o2) {
...
}
}
class TheThread extends Thread{
...
}
class TheThread1 extends Thread{
...
}

运行结果

wait及notify

wait的作用就是让使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。这时其它线程可抢夺控制权

注意:一旦它重新获得对象的同步请求,所有之前的请求状态都 会恢复,也就是线程调用wait的地方的状态。线程将会在之前调用wait的地方继续运行下去。

另一个会导致线程暂停的方法还有 Thread 类的 sleep 方法,它会导致线程睡眠指定 的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的

notify的作用就是唤醒请求队列中的一个线程,而notifyAll唤醒的是请求队列中的所有线程

注意:被唤醒的线程不会马上运行,除非获取了该Object的锁。也就是说,调用notify的线程,在调用notify后,不会像wait一样,马上阻塞线程的运行。而是继续运行,直到相应的线程调度完成或者让出Object的锁。而被唤醒的线程会在当前线程让出Object锁后,与其他线程以常规的方式竞争对象锁。

例子:

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
public class MainTest {
public static void main(String[] args) {
// TODO 自动生成的方法存根
Sample sample = new Sample();
Thread t1 = new IncreaseThread(sample);
Thread t2 = new DecreaseThread(sample);
Thread t3 = new IncreaseThread(sample);
Thread t4 = new DecreaseThread(sample);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Sample {
private int number;
public synchronized void increase() {
while (0 != number) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
System.out.println(number);
notify();
}
public synchronized void decrease() {
while (0 == number) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
System.out.println(number);
notify();
}
}
class IncreaseThread extends Thread {
private Sample sample;
public IncreaseThread(Sample sample) {
this.sample = sample;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep((long) (Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
sample.increase();
}
}
}
class DecreaseThread extends Thread {
private Sample sample;
public DecreaseThread(Sample sample) {
this.sample = sample;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep((long) (Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
sample.decrease();
}
}
}

运行结果