多线程基础

藏好自己,做好清理

实际上是多个CPU,即多核,如果一个CPU情况下,其实是一个时刻处理一个线程,一段时间内处理多个线程

1. 进程与线程

进程是程序的一次动态执行过程,它经历了从代码加载(产生)、执行(发展)到执行完毕(消亡)的一个完整过程。每个进程都能循环获得自己的CPU时间片,CPU执行速度非常快,所以好像同时运行一样。

1.1 打开WPS是打开一个程序,也相当于启动了一个进程,WPS的拼音检查就是一个线程,可以有很多的线程在进程上,PID的P就是Process【进程】

1.2 在一个进程中,自己没有创建线程,后台也会有多个线程,如main()【主线程】,gc线程【垃圾回收线程】

1.3 单线程:run()方法先执行完再返回到主方法

1.4 多线程:start()方法和主方法一起并发执行(时间段);在时刻上还是执行其中一个

2. 多线程实现

2.1 继承Thread类(java.lang.Thread)

  • 栗子1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//创建线程方式一:继承Thread类,重写run方法,调用start开启线程
package com.xxy.thread;
//创建线程方式一:继承Thread类,重写run方法,调用start开启线程
public class TestThread extends Thread{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 10; i++) {
System.out.println("我在看代码" + i);
}
}

public static void main(String[] args) {
//main方法线程,主线程
TestThread testThread = new TestThread();
//调用start()开启线程
testThread.start();
//testThread.run();
for (int i = 0; i < 10; i++) {
System.out.println("我在学习多线程" + i);
}
}
}
  • 栗子2:下载多张图片

下载Apache Commons IO

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
package com.xxy.thread;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

//实现多线程下载图片
public class TestThreadDownload extends Thread{
private String url;
private String name;

public TestThreadDownload(String url,String name) {
this.url = url;
this.name = name;
}
//下载图片执行体
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载了文件名为:" + name);
}

public static void main(String[] args) {
TestThreadDownload t1 = new TestThreadDownload("https://img-home.csdnimg.cn/images/20210624094420.png","1.png");
TestThreadDownload t2 = new TestThreadDownload("https://img-bss.csdn.net/1623287748640.png","2.png");
TestThreadDownload t3 = new TestThreadDownload("https://img-home.csdnimg.cn/images/20210426034059.png","3.png");
t1.start();
t2.start();
t3.start();
}
}

class WebDownloader {
public void downloader(String url,String name) {
try {
FileUtils.copyURLToFile(new URL(url),new File(name));

} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}

  • 为什么线程启动必须调用start()方法执行而不是直接调用run()方法

start()方法源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public synchronized void start() {
if (this.threadStatus != 0) {
throw new IllegalThreadStateException();//运行时异常
} else {
this.group.add(this);
boolean var1 = false;

try {
this.start0();//方法上使用了native关键字(java本地接口调用)调用本机操作系统的函数功能完成特殊操作
var1 = true;
} finally {
try {
if (!var1) {
this.group.threadStartFailed(this);
}
} catch (Throwable var8) {
}

}

}
}
1
private native void start0();

为了java的可移植性,只在意调用操作系统的函数功能来实现start0()方法

  • 缺点

单继承局限

2.2 实现Runnable接口

避免了单继承的局限性

  • Runnable接口源码
1
2
3
4
5
6
7
8
9
10
11
12
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package java.lang;

@FunctionalInterface //Jdk1.8引入Lambda表达式变为函数式接口
public interface Runnable {
void run();
}

  • 栗子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
package com.xxy.thread;
//创建线程方法2:实现Runnable接口,重写run方法,执行线程时需要丢入Runnable接口实现类,调用strat方法
public class TestThread2 implements Runnable {
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 10; i++) {
System.out.println("我在看代码" + i);
}
}

public static void main(String[] args) {
//main方法线程,主线程

TestThread2 testThread2 = new TestThread2();
//创建一个线程对象,通过线程对象来开启我们的线程,代理
//Thread thread = new Thread(testThread2);
//thread.start();

//调用start()开启线程
new Thread(testThread2).start();
for (int i = 0; i < 10; i++) {
System.out.println("我在学习多线程" + i);
}
}
}

  • 栗子2:lambda表达式,jdk1.8新特性
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 com.xxy.thread;
//创建线程方法2:实现Runnable接口,重写run方法,执行线程时需要丢入Runnable接口实现类,调用strat方法
public class TestThread3 implements Runnable {
@Override
public void run() {

}

public static void main(String[] args) {
//main方法线程,主线程

//调用start()开启线程 ,lambda表达式
new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("我在看代码" + i);
}
}).start();

for (int i = 0; i < 10; i++) {
System.out.println("我在学习多线程" + i);
}
}


}

  • 栗子3:多线程下载图片
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 com.xxy.test;

import com.xxy.thread.TestThreadDownload;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

//实现多线程下载图片
public class TestRunableDownload implements Runnable{
private String url;
private String name;

public TestRunableDownload(String url,String name) {
this.url = url;
this.name = name;
}
//下载图片执行体
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载了文件名为:" + name);
}

public static void main(String[] args) {
TestThreadDownload t1 = new TestThreadDownload("https://img-home.csdnimg.cn/images/20210624094420.png","1.png");
TestThreadDownload t2 = new TestThreadDownload("https://img-bss.csdn.net/1623287748640.png","2.png");
TestThreadDownload t3 = new TestThreadDownload("https://img-home.csdnimg.cn/images/20210426034059.png","3.png");

new Thread(t1).start();
new Thread(t2).start();
new Thread(t3).start();
}
}

class WebDownloader {
public void downloader(String url,String name) {
try {
FileUtils.copyURLToFile(new URL(url),new File(name));

} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}

  • 栗子4:并发问题
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
package com.xxy.thread;


//卖车票
//发现问题:并发,数据乱
public class TestThread4 implements Runnable{
private int tickNums = 10;
@Override
public void run() {
while (true) {
if (tickNums <= 0) {
break;
}

try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println(Thread.currentThread().getName() + "-->拿到了第" + (tickNums--) + "票");

}
}

public static void main(String[] args) {
TestThread4 ticket = new TestThread4();

new Thread(ticket,"小明").start();
new Thread(ticket,"老师").start();
new Thread(ticket,"小李").start();


}
}

  • 栗子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
package com.xxy.thread;
//模拟龟兔赛跑
public class TestRace implements Runnable{
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子休息
if (Thread.currentThread().getName().equals("兔子") && i % 20 == 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
if (flag) {
break;
}
System.out.println(Thread.currentThread().getName() + "-->跑了" + i + "步");

}
}

//判断是否完成比赛
private boolean gameOver(int steps) {
//判断是否有胜利者
if (winner != null) {
return true;
}else if (steps >= 100) {
winner = Thread.currentThread().getName();
System.out.println("胜利者是" + winner);
return true;
}

return false;
}

public static void main(String[] args) {
TestRace testRace = new TestRace();
new Thread(testRace,"兔子").start();
new Thread(testRace,"乌龟").start();
}
}

2.3 实现Callable接口

可以有返回值,有call()方法,需要抛出异常

  • Callable接口源码
1
2
3
4
5
6
7
8
9
10
11
12
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package java.util.concurrent;

@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}

  • 栗子:
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
package com.xxy.thread;


import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;


//创建方式三:实现Callable接口
public class TestCallable implements Callable<Boolean> {
private String url;
private String name;

public TestCallable(String url,String name) {
this.url = url;
this.name = name;
}

//下载图片执行体
@Override
public Boolean call() throws Exception {
WebDownloader1 webDownloader1 = new WebDownloader1();
webDownloader1.downloader(url,name);
System.out.println("下载了文件名为:" + name);
return true;
}

public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1 = new TestCallable("https://img-home.csdnimg.cn/images/20210624094420.png","4.png");
TestCallable t2 = new TestCallable("https://img-bss.csdn.net/1623287748640.png","5.png");
TestCallable t3 = new TestCallable("https://img-home.csdnimg.cn/images/20210426034059.png","6.png");

//创建执行服务
ExecutorService service = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = service.submit(t1);
Future<Boolean> r2 = service.submit(t2);
Future<Boolean> r3 = service.submit(t3);
//获取结果
Boolean rs1 = r1.get();
Boolean rs2 = r2.get();
Boolean rs3 = r3.get();
//关闭服务
service.shutdownNow();

}

}

class WebDownloader1 {
public void downloader(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));

} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}

3. 多线程操作方法

3.1 多线程运行状态

注意:线程开启不一定立即执行,有可能为就绪状态,由cpu调度执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
graph LR
A[new创建] -->|start方法| B(线程锁)
B --> C(就绪状态)
C -->|CPU调度| D(运行状态)
D -->|礼让yield| D
D -->|等待wait| I(等待状态)
I -->|唤醒notify| C
I -->|销毁stop0| H[终止]
D -->|暂停方法suspend0| F(挂起状态)
F -->|销毁stop0| H[终止]
F -->|恢复执行resume0| C
D -->|产生阻塞事件,sleep| E(阻塞状态)
E -->|解除阻塞join| C
E -->|销毁stop0| H[终止]

  • 栗子
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 com.xxy.thread;

public class TestThreadState {
public static void main(String[] args) {
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("////");

});

//观察状态
Thread.State state = thread.getState();
System.out.println(state);//NEW

//观察启动后
thread.start();
state = thread.getState();
System.out.println(state);//RUN

while (state != Thread.State.TERMINATED){//只要线程不终止一直输出
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
state = thread.getState();
System.out.println(state);
}

}
}

3.2 操作方法

线程的命名和取得
方法 类型 说明
public Thread(Runnable var1, String var2) 构造 实例化线程对象,接收Runnable接口子类对象,同时设置线程名字
public final synchronized void setName(String var1) 普通 设置线程名字
public final String getName() 普通 取得线程名字
public static native Thread currentThread() 普通 获取当前线程对象

4. 线程休眠(sleep())

每一个对象都有一个锁,sleep不会释放锁

线程休眠sleep方法
方法 类型 说明
public static native void sleep(long var0) throws InterruptedException 普通 设置线程休眠的毫秒数,时间一到自动唤醒
public static void sleep(long var0, int var2) throws InterruptedException 普通 设置线程休眠的毫秒数与纳秒数,时间一到自动唤醒

5. 线程中断(interrupt())

一般不会使用此方法中断线程

线程中断的操作方法
方法 类型 说明
public boolean isInterrupted() 普通 判断线程是否被中断
public void interrupt() 普通 中断线程执行,别用这个方式
public final native boolean isAlive() 普通 测试线程是否处于活动状态

6. 线程强制执行(join())

线程强制执行的操作方法
方法 类型 说明
public final void join() throws InterruptedException 普通 可以强制执行,等它执行后其他线程再执行
  • 栗子
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 com.xxy.thread;



//测试join,想象为插队
public class TestThreadJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程VIP来了");
}
}

public static void main(String[] args) throws InterruptedException {
TestThreadJoin testThreadJoin = new TestThreadJoin();
Thread thread = new Thread(testThreadJoin);
thread.start();
//主线程
for (int i = 0; i < 10; i++) {
if(i == 5){
thread.join();
}
System.out.println("main" + i);
}
}
}

7. 线程礼让(yield())

让cpu重新调度,不一定礼让成功

线程礼让的方法
方法 类型 说明
public static native void yield() 普通 线程礼让
  • 栗子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.xxy.thread;
//测试线程礼让
//礼让不一定成功,看cpu重新调度心情,
public class TestThreadYied {
public static void main(String[] args) {
MyYied myYied = new MyYied();
new Thread(myYied,"a").start();
new Thread(myYied,"b").start();
}
}
class MyYied implements Runnable{

@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName() + "线程停止执行");
}
}

8. 线程优先级(setPriority())

优先级低意味着获得CPU调度的概率低,并不是不会调用,有可能优先级低在优先级高的前面调用

线程优先级的方法
方法 类型 说明
public static final int MIN_PRIORITY = 1 常量 最高优先级,数值为10
public static final int NORM_PRIORITY = 5 常量 中等优先级,数值为5
public static final int MAX_PRIORITY = 10 常量 最低优先级,数值为1
public final void setPriority(int var1) 普通 设置线程优先级
public final int getPriority() 普通 取得线程优先级
  • 栗子
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
package com.xxy.thread;
//测试线程优先级,优先级高的不是一定先执行
public class TestPriority {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority);
Thread t2 = new Thread(myPriority);
Thread t3 = new Thread(myPriority);
Thread t4 = new Thread(myPriority);
Thread t5 = new Thread(myPriority);
Thread t6 = new Thread(myPriority);

//设置优先级,在启动
t1.start();

t2.setPriority(1);
t2.start();

t3.setPriority(4);
t3.start();

t4.setPriority(Thread.MAX_PRIORITY);//10
t4.start();
//报错
/* t5.setPriority(-1);
t5.start();*/

t6.setPriority(8);
t6.start();



}

}
class MyPriority implements Runnable{

@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
}
}

9. 同步与死锁(synchronized、lock)

9.1 同步

多个线程操作同一个资源,队列+锁来实现

每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

9.2 同步不安全栗子

9.3 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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.xxy.syc;
//不安全买票
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();

new Thread(buyTicket,"我").start();
new Thread(buyTicket,"你").start();
new Thread(buyTicket,"他").start();
}
}
class BuyTicket implements Runnable {
//票
private int tickeNums = 10;
boolean flag = true;
@Override
public void run() {
//买票
while (flag) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
buy();
}
}

private synchronized void buy() {
//判断是否有票
if (tickeNums <= 0) {
flag = false;
return;
}
//模拟延迟
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//买票
System.out.println(Thread.currentThread().getName() + "--> 拿到" + (tickeNums--));
}
}
  • 锁代码块
  • 栗子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
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
package com.xxy.syc;
//不安全的取钱

public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(100,"结婚基金");
Drawing you = new Drawing(account,50,"你");
Drawing yourWife = new Drawing(account,100,"你的妻子");
you.start();
yourWife.start();
}
}
//账户
class Account {
String name; //m名字
int money;//金额

public Account(int money,String name) {
this.name = name;
this.money = money;
}

}
//银行:模拟取款
class Drawing extends Thread {
Account account;
int drawingMoney;
int nowMoney;

public Drawing( Account account,int drawingMoney,String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;

}
//取钱
@Override
public void run() {

//锁的对象就是增删改
synchronized (account) {
//判断有没有钱
if (account.money - drawingMoney < 0) {
System.out.println(Thread.currentThread().getName() + "余额不足");
return;
}
//sleep可以放大问题的发生性
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡内余额
account.money = account.money - drawingMoney;
//你手里的钱
nowMoney = nowMoney + drawingMoney;

System.out.println(account.name + "的余额为:" + account.money);
//Thread.currentThread().getName() == this,getName()
System.out.println(this.getName() + "手里的钱" + nowMoney);
}
}

}
  • 栗子二:
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 com.xxy.syc;

import java.util.ArrayList;
import java.util.List;

//线程不安全的集合
public class UnsafeUnit {
public static void main(String[] args) {
//不安全的原因:有两个或者一个操作了同一个位置
List<String> list = new ArrayList<String>();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}

  • 栗子3;JUC安全类型集合
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.xxy.syc;

import java.util.concurrent.CopyOnWriteArrayList;
//测试JUC安全类型的集合
public class TestJUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
list.add(Thread.currentThread().getName());
}).start();
}

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println(list.size());
}
}

缺陷:将一个大的方法声明为synchronized,将会极大影响性能

9.4 死锁

两个或多个线程各自等待对方的资源或释放资源,都处于等待的情况

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
package com.xxy.syc;



public class DeadLock {
public static void main(String[] args) {
Makeup m1 = new Makeup(0,"li");
Makeup m2 = new Makeup(1,"lhh");
m1.start();
m2.start();
}
}

//口红
class Lipstick {

}
//镜子
class Mirror {

}
//化妆
class Makeup extends Thread {
//保证需要的资源只有一份
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;//选择
String girlName;//女孩名字

public Makeup(int choice,String girlName) {
this.choice = choice;
this.girlName = girlName;
}

//化妆
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

//化妆,互相等待对方的资源
private void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) {
System.out.println( this.girlName + "获取口红的锁");
Thread.sleep(1000);
//一秒后获取镜子
synchronized (mirror) {
System.out.println(this.girlName + "获取镜子的锁");
}
}
/* //一秒后获取镜子,解决方法:破坏请求与保持条件
synchronized (mirror) {
System.out.println(this.girlName + "获取镜子的锁");
}*/
} else {

synchronized (mirror) {
System.out.println(this.girlName + "获取镜子的锁");
Thread.sleep(1000);
//一秒后获取口红
synchronized (lipstick) {
System.out.println( this.girlName + "获取口红的锁");

}
}
/* //一秒后获取口红,解决方法:
synchronized (lipstick) {
System.out.println( this.girlName + "获取口红的锁");

}*/


}
}

}
  • 显示定义同步锁(Lock)

java.util.concurrent.lock接口控制多个线程对共享资源进行访问

只有代码块锁,性能比隐式锁更好,需要自己释放锁

  • 栗子
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
package com.xxy.syc;

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
public static void main(String[] args) {
TestLock2 testLock2 = new TestLock2();
new Thread(testLock2).start();
new Thread(testLock2).start();
new Thread(testLock2).start();
}

}


class TestLock2 implements Runnable {
int tickNums = 10;
//定义Lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();//加锁
try {
if (tickNums > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(tickNums--);
} else {
break;
}
} finally {
//解锁
lock.unlock();
}

}
}
}

10. 停止线程

stop()、suspend()、resume()在jdk1.2已经不再使用,wait(),notify()属于Object类,不属于Thread类,推荐线程自己停止下来,或者标志位

Thread类停止线程操作方法
方法 类型 说明
private native void stop0(Object var1) 普通 停止多线程
private native void suspend0() 普通 挂起线程、暂停执行
private native void resume0() 普通 恢复挂起的线程执行
Object类停止线程

注意:都是Object类停止或唤醒线程,只能在同步方法或者同步代码块中使用,否则抛出异常

方法 类型 说明
public final void wait() throws InterruptedException 普通 线程的等待
public final native void wait(long var1) throws InterruptedException 普通 设置线程等待的毫秒数
public final void wait(long var1, int var3) throws InterruptedException 普通 设置线程等待的毫秒数和纳秒数
public final native void notify() 普通 唤醒第一个等待的线程
public final native void notifyAll() 普通 唤醒所有等待的线程
  • 栗子:
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
package com.xxy.thread;
//测试stop
//建议线程正常停止 --> 利用次数,不建议死循环
//建议使用标志位 --> 设置一个标志位
//不要使用stop或destroy等过时或者jdk不建议使用的方法
public class TestThreadStop implements Runnable {
//1.设置一个标志位
private boolean flag = true;

@Override
public void run() {
int i = 0;
while (flag){
System.out.println("运行线程" + i++);
}
}
//2.设置一个公开的方法停止线程,转换标志位
public void stop(){
this.flag = false;
}

public static void main(String[] args) {
TestThreadStop testThreadStop = new TestThreadStop();
new Thread(testThreadStop).start();
for (int i = 0; i < 10; i++) {
System.out.println("main" + i);
if( i == 9){
testThreadStop.stop();
System.out.println("线程停止了");
}
}
}
}

11. 后台守护线程(daemon)

守护用户进程,例如GC进程,记录日志

守护进程的操作方法
方法 类型 说明
public final void setDaemon(boolean var1) 普通 设置为守护进程
public final boolean isDaemon() 普通 判断是否为守护进程
  • 栗子
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
package com.xxy.thread;

public class TestDaemon {
public static void main(String[] args) {
God god = new God();
You you = new You();

Thread thread = new Thread(god);
thread.setDaemon(true);//false默认是用户进程
thread.start();
new Thread(you).start();//用户进程启动
}
}

//上帝
class God implements Runnable {

@Override
public void run() {
//当用户进程结束后,守护进程会随后结束
while (true) {
System.out.println("自己保护自己吧");
}
}
}
//你
class You implements Runnable {

@Override
public void run() {
for (int i = 0; i < 36; i++) {
System.out.println("一生都开心的活着");
}

System.out.println("good bye world!");
}
}

12. volatile关键字

volatile关键字不是描述同步的操作,而是可以更快捷的进行原始变量的访问,避免了副本创建和数据同步处理,事务的原子性用的比较多

13. 静态代理

  • 栗子1:
  • Marry接口
1
2
3
4
5
6
package com.xxy.test;

public interface Marry {
void HappyMarry();
}

  • You类
1
2
3
4
5
6
7
8
9
10
11
package com.xxy.test;

public class You implements Marry {
@Override
public void HappyMarry() {
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
package com.xxy.test;

public class WoddingCompany implements Marry {
private Marry target;

public WoddingCompany(Marry target) {
this.target = target;
}

@Override
public void HappyMarry() {
before();
this.target.HappyMarry();
after();
}

private void after() {
System.out.println("结婚之后,收尾款");
}

private void before() {
System.out.println("结婚之前,布置现场");
}
}


  • 主方法
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.xxy.test;

/**
*
*/
public class StaticProxy {
public static void main(String[] args) {
You you = new You();
WoddingCompany woddingCompany = new WoddingCompany(you);
woddingCompany.HappyMarry();
}
}

14. Lambda表达式(jdk1.8新特性之一)

14.1 函数式接口

定义:任何一个接口只包含唯一的抽象方法,可以有多个非抽象方法,叫函数式接口,lambda就是匿名内部类

  • 栗子1:

  • 函数式接口

1
2
3
4
5
6
package com.xxy.lambda;

public interface ILike {
void lambda();
}

  • 实现类
1
2
3
4
5
6
7
8
9
package com.xxy.lambda;

public class Like implements ILike {
@Override
public void lambda() {
System.out.println("我喜欢 Lambda1");
}
}

  • 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
package com.xxy.lambda;

public class TestLambda {
//1. 静态内部类
static class Like2 implements ILike {

@Override
public void lambda() {
System.out.println("你猜我喜不喜欢?2");
}
}
public static void main(String[] args) {
ILike iLike = new Like();
iLike.lambda();

iLike = new Like2();
iLike.lambda();

//2. 局部内部类
class Like3 implements ILike {

@Override
public void lambda() {
System.out.println("我不猜3");
}
}

iLike = new Like3();
iLike.lambda();

//3. 匿名内部类,没有类的名字,必须借助接口或者父类
iLike = new ILike() {
@Override
public void lambda() {
System.out.println("我猜4");
}
};
iLike.lambda();

//4. lambda简化
iLike = (() -> {
System.out.println("我喜欢吗5");
});
iLike.lambda();

}
}

15. 生产消费者问题

  • 管程法:利用缓冲区
  • 信号灯法:标志位解决

16. 线程池

避免频繁销毁和创建

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
package com.xxy;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

//测试线程池
public class TestPool {
public static void main(String[] args) {
//1. 创建服务,创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//执行
service.execute(new MyPool1());
service.execute(new MyPool1());
// 2. 关闭连接
service.shutdown();
}
}

class MyPool1 implements Runnable {

@Override
public void run() {

System.out.println(Thread.currentThread().getName());

}
}

17. 总结

总结多线程的基础,还没有线程池的内容。如果有错误和建议,欢迎评论指出。