JavaSE 进阶(十八)

静态代理

结婚案例

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
package ml.guest997;

public class StaticProxy {
public static void main(String[] args) {
Company company = new Company(new Person()); //静态代理对象的创建需要传入真实对象
company.work();
}
}

//结婚接口
interface Marry {
void work();
}

//人(真实对象)实现结婚接口
class Person implements Marry {

@Override
public void work() {
System.out.println("人结婚了");
}

}

//公司(代理对象)也实现结婚接口
class Company implements Marry {

//代理的真实对象
private Person person;

public Company(Person person) {
this.person = person;
}

@Override
public void work() {
//代理对象可以做很多真实对象做不了的事情,这样真实对象就能专注做自己的事。
before();
person.work();
after();
}

private void before() {
System.out.println("公司处理结婚前事务");
}

private void after() {
System.out.println("公司处理结婚后事务");
}

}
/*结果为
公司处理结婚前事务
人结婚了
公司处理结婚后事务
*/

可以看到代理对象的创建类似于线程创建时传了个实现 Runnable 接口的类对象。

Lambda 表达式

为什么要使用 Lambda 表达式?

  • 避兔匿名内部类定义过多
  • 可以让代码看起来很简洁
  • 只留下核心的逻辑

什么时候能够使用 Lambda 表达式?

  • 创建函数式接口对象的时候。

什么是函数式接口?

  • 接口只包含唯ー一个抽象方法。

以前的代码实现

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
package ml.guest997;

public class LambdaTest01 {

//静态内部类
static class Talk02 implements ITalk {
@Override
public void talk() {
System.out.println("talk02");
}
}

public static void main(String[] args) {
new Talk01().talk();
new Talk02().talk();

//局部内部类
class Talk03 implements ITalk {
@Override
public void talk() {
System.out.println("talk03");
}
}
new Talk03().talk();

//匿名内部类
ITalk talk04 = new ITalk() {
@Override
public void talk() {
System.out.println("talk04");
}
};
talk04.talk();
}

}

//函数式接口
interface ITalk {
void talk();
}

//实现类
class Talk01 implements ITalk {

@Override
public void talk() {
System.out.println("talk01");
}

}
/*结果为
talk01
talk02
talk03
talk04
*/

有些代码只会使用到一次,之前就能使用如上面的方法来简化代码。而 java8 之后提供了新的方式来简化代码。

Lambda 简化代码

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 ml.guest997;

public class LambdaTest02 {
public static void main(String[] args) {
//标准 Lambda 语法
ISay say01 = (String name) -> {
System.out.println(name + " say01");
};
say01.say("01");

//简化一:去掉全部参数类型
ISay say02 = (name) -> {
System.out.println(name + " say02");
};
say02.say("02");

//简化二:去掉括号(需要只有一个参数)
ISay say03 = name -> {
System.out.println(name + " say03");
};
say03.say("03");

//简化三:去掉花括号(需要代码只有一行)
ISay say04 = name -> System.out.println(name + " say04");
say04.say("04");
}
}

//函数式接口
interface ISay {
void say(String name);
}
/*结果为
01 say01
02 say02
03 say03
04 say04
*/

可以看到通过 Lambda 简化后的代码甚至可以短到只有一行。

线程状态

线程只能启动一次,一旦死亡就不能再次启动。

线程停止

不推荐使用 JDK 提供的方法。推荐让线程自己停下来。使用一个标志位当作终止变量,如当 flag = false 时线程终止。

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 ml.guest997;

public class StopTest implements Runnable {

private boolean flag = true;

public static void main(String[] args) {
StopTest stopTest = new StopTest();
new Thread(stopTest).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main" + i);
if (i == 500) {
stopTest.stop();
}
}
}

@Override
public void run() {
int i = 0;
while (flag) {
System.out.println("running" + i++);
}
}

private void stop() {
this.flag = false;
System.out.println("线程停止了");
}
}

线程休眠

休眠可以模拟网络延时和倒计时。每一个对象都有一个锁,休眠不会释放锁。

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
package ml.guest997;

import java.text.SimpleDateFormat;
import java.util.Date;

public class SleepTest {
public static void main(String[] args) {
SleepTest sleepTest = new SleepTest();
System.out.println("每隔一秒输出当前时间");
sleepTest.nowTime();
System.out.println("十秒倒计时");
sleepTest.countDown();
}

public void nowTime() {
Date date = new Date(System.currentTimeMillis());
int i = 0;
while (i < 5) {
try {
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));
Thread.sleep(1000);
date = new Date(System.currentTimeMillis());
i++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public void countDown() {
try {
for (int i = 10; i >= 0; i--) {
System.out.println(i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}
/*结果为
每隔一秒输出当前时间
2021-11-28 00:42:45
2021-11-28 00:42:46
2021-11-28 00:42:47
2021-11-28 00:42:48
2021-11-28 00:42:49
十秒倒计时
10
9
8
7
6
5
4
3
2
1
0
*/

线程礼让

线程礼让就是让运行中的线程暂停,但不是进入阻塞状态,而是进入就绪状态,与其它线程回到同一起跑线。所以礼让不一定会成功,看 CPU 的调度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package ml.guest997;

public class YieldTest implements Runnable {

public static void main(String[] args) {
YieldTest yieldTest = new YieldTest();
new Thread(yieldTest, "A").start();
new Thread(yieldTest, "B").start();
}

@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始");
Thread.yield();
System.out.println(Thread.currentThread().getName() + "线程结束");
}

}
/*结果为
A线程开始
B线程开始
A线程结束
B线程结束
*/

线程合并

线程合并可以想象为插队,只有当插队的线程运行完才能运行其它线程,其它线程会处于阻塞状态。

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
package ml.guest997;

public class JoinTest implements Runnable {

@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("使用线程" + i);
}
}

public static void main(String[] args) {
JoinTest joinTest = new JoinTest();
Thread thread = new Thread(joinTest);
thread.start();
try {
for (int i = 0; i < 10; i++) {
System.out.println("使用 main 线程" + i);
if (i == 5) {
thread.join();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}
/*结果为
使用 main 线程0
使用 main 线程1
使用 main 线程2
使用 main 线程3
使用 main 线程4
使用 main 线程5
使用线程0
使用线程1
使用线程2
使用线程3
使用线程4
使用线程5
使用线程6
使用线程7
使用线程8
使用线程9
使用 main 线程6
使用 main 线程7
使用 main 线程8
使用 main 线程9
*/

线程优先级

线程优先级从1到10,1最低,10最高。当线程的优先级没有指定时,所有线程都为普通优先级(5)。

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
package ml.guest997;

public class PriorityTest implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}

public static void main(String[] args) {
System.out.println("main 线程优先级:" + Thread.currentThread().getPriority());
Thread t1 = new Thread(new PriorityTest(), "one");
Thread t2 = new Thread(new PriorityTest(), "two");
Thread t3 = new Thread(new PriorityTest(), "three");
t1.setPriority(6);
t3.setPriority(4);
t1.start();
t2.start();
t3.start();
}
}
/*结果为
main 线程优先级:5
one0
two0
one1
two1
one2
two2
one3
two3
one4
two4
one5
two5
one6
two6
two7
one7
two8
two9
one8
one9
three0
three1
three2
three3
three4
three5
three6
three7
three8
three9
*/

可以从结果看出,不是高优先级的线程就一定先比低优先级的线程先运行完,而是高优先级的线程先运行的概率比低优先级的线程高。

守护线程

线程分为用户线程和守护线程,虚拟机必须确保用户线程执行完毕,但是不用等待守护线程执行完毕。守护线程有如后台记录操作日志、监控内存和垃圾回收等待。

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
package ml.guest997;

public class DaemonTest {
public static void main(String[] args) {
Thread daemonThread = new Thread(() -> {
while (true) {
System.out.println("守护线程一直运行");
}
});

Thread userThread = new Thread(() -> {
for (int i = 0; i < 3; i++) {
System.out.println("用户线程运行" + i);
}
});

userThread.setPriority(10);
userThread.start();
daemonThread.setDaemon(true); //设置线程为守护线程
daemonThread.start();
}
}
/*结果为
用户线程运行0
用户线程运行1
用户线程运行2
守护线程一直运行
守护线程一直运行
守护线程一直运行
守护线程一直运行
守护线程一直运行
守护线程一直运行
守护线程一直运行
守护线程一直运行
守护线程一直运行
守护线程一直运行
守护线程一直运行
守护线程一直运行
守护线程一直运行
守护线程一直运行
守护线程一直运行
守护线程一直运行
守护线程一直运行
守护线程一直运行
守护线程一直运行
守护线程一直运行

Process finished with exit code 0
*/