Java线程复习

本文总阅读量

join()操作:

在主程序中,线程.join()操作,会使主程序(主线程)等待线程执行结束才继续向下执行。
相当于方法调用

yield()操作:

当在这个线程中调用了yield()之后,当前线程会让出CPU资源一小会,让别的线程去执行。

举例<( ̄︶ ̄)>:

你在厕所坑位前准备上厕所,来了一个人急的马上要拉到裤子上了。你调用一下yield(),说:“你先来吧,我先让你上厕所!”

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
56
57
/*
* TestSynchronized2.java
*
* Created on: 2016年3月16日 下午8:25:11
* Author: Wayne 13186259527@163.com
*/


/**
* @author Administrator
*
*/

public class TestSynchronized2 implements Runnable {
int b = 100;

public synchronized void m1() throws Exception{
//Thread.sleep(2000);
System.out.println("================");
System.out.println("我进入了m1()");

b = 1000;
System.out.println("我在m1()");
Thread.sleep(5000);
System.out.println("我等了5秒");
System.out.println("m1:b = " + b);
System.out.println("我离开了m1()");
System.out.println("================");
}

public synchronized void m2() throws Exception {
System.out.println("================");
System.out.println("我进入了m2()");
Thread.sleep(5500);
System.out.println("我等了5.5秒");
System.out.println("我在m2()");
b = 2000;
System.out.println("m2: b= "+b);
System.out.println("我离开了m2()");
System.out.println("================");
}

public void run() {
try {
m1();
} catch(Exception e) {
e.printStackTrace();
}
}

public static void main(String[] args) throws Exception {
TestSynchronized2 tt = new TestSynchronized2();
Thread t = new Thread(tt);
t.start();

tt.m2();
System.out.println("tt.b="+tt.b);
}
}

结果:

同一个对象的2个线程去分别访问该对象的两个synchronized方法,结果是一个执行结束后,再执行另外一个。印证了上面的说法。

synchronized块:

在执行当前代码块过程中,当前代码块被锁定,其他线程不能访问这个代码块。
但是其他的部分(未被synchronized(this)修饰的代码)可以被其他线程访问。(亲测有效)
以下例子测试未被synchronized(this)修饰的代码是否可以被其他线程访问

  • 主线程开启了一个t的线程,调用(被synchronized修饰的)m1()。在里面sleep()5秒钟。然后把b赋为1000。
  • 主线程自己调用该类中(未被synchronized修饰的)m2(),把b赋为2000。
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
/*
* TestSynchronized.java
*
* Created on: 2016年3月16日 下午8:02:40
* Author: Wayne 13186259527@163.com
*/


/**
* @author Administrator
*
*/

public class TestSynchronized implements Runnable {
int b = 100;

public synchronized void m1() throws Exception {
Thread.sleep(5000);
b = 1000;
System.out.println("b = " + b);
}

public void m2() throws Exception {
b = 2000;
}

public void run() {
try {
m1();
} catch (Exception e) {
e.printStackTrace();
}
}

public static void main(String[] args) throws Exception {
TestSynchronized tt = new TestSynchronized();
Thread t = new Thread(tt);
t.start();

tt.m2();
System.out.println(tt.b);
}
}

结果如下:

证实:未被synchronized修饰的代码,可以在被加锁的代码执行时可以被其他线程访问。

sleep()操作

在程序中Thread.sleep(毫秒)
可以让程序睡眠毫秒的时长。

但是

如果是在synchronized修饰的代码块(或者方法)中,有sleep操作,当前的锁不会被释放。知道被锁定的代码块(或者方法)执行完毕,锁才会被释放。

即:sleep():睡着了,仍然抱着锁。

wait()操作

这是java.lang.object类里面的操作,换句话说:这是所有类的老祖宗拥有的方法,所以所有对象都可以调用这个方法。

  • 例如:
    this.wait();
    表示:在当前的正在访问这个对象的线程wait。
  • wait()之后,之前此线程拥有的锁,就不在归它所有了。锁被释放了。

强调:

wait()操作必须和synchronized()一起使用。

即,这个线程只有拥有了一把锁(锁方法,或者锁代码块),才可以wait()。
在代码层面:wait()方法必须在synchronized(){…}语句块内被调用。

notify()操作

this.notify():叫醒一个正在当前对象上wait的线程。

  • notify()和wait()一般是一一对应的。

notifyAll()操作

this.notifyAll():叫醒当前wait在此对象上的所有线程。

但是:例如有3个线程A,B,C wait()在该对象上,调用了notifyAll()后,也只有一个线程获得锁继续执行,等它执行完后,其他2个才一个一个地执行(其中的顺序应该是由JVM决定的)

以下是引用一个仁兄在百度上的回答,觉得很有道理:


调用notifyAll通知所有线程继续执行,只能有一个线程在执行其余的线程在等待(因为在所有线程被唤醒的时候在synchornized块中)。这时的等待和调用notifyAll前的等待是不一样的。

notifyAll前:在对象上休息区内休息

notifyAll后:在排队等待获得对象锁。

notify和notifyAll都是把某个对象上休息区内的线程唤醒,notify只能唤醒一个,但究竟是哪一个不能确定,而notifyAll则唤醒这个对象上的休息室中所有的线程。


sleep()和wait()的重大区别:

sleep:睡着了,抱着锁

* sleep()是Thread类的方法,sleep()执行时,(如果锁定了对象)别的线程不能访问被锁定对象。

wait: 睡着了,放开锁

* wait()是Object类的方法,wait()执行时,(必须是在锁定对象里调用wait)别的线程可以访问被锁定对象。

场景应用:
对于一个数据库文件,读操作是可以让多线程同时访问的(读一下又不会怀孕(๑•ᴗ•๑)),但是写操作应该是要加锁的,否则就会出现数据不一致等问题。

类比:大街上看见一美女,看操作是可以让很多人(多线程)同时看的(又不犯法),但是搭讪操作应该是要加锁的,否则你让美女跟哪个人说话,美女不就乱套了吗?
P.S 当然更不能摸。