0%

Java-线程学习笔记(上)

线程学习笔记(上)

目录:

  • 程序 进程 线程
  • Java实现多线程
  • 线程的状态和方法
  • 线程基本信息和优先级
  • 线程的同步和死锁问题
  • 生产者消费模式
  • 任务调度

线程的概念

程序 进程 线程

  1. 程序(Program):指令集 静态概念
  2. 进程(Process):操作系统 调度程序 动态概念
  3. 线程(Thread):在进程内多条执行路径

  线程是进程中的一个“单一的连续控制流程”/执行路径

  • 线程又被成为轻量级进程
  • Thread run at the same time,independently of one another
  • 一个进程可拥有多个并行的线程
  • 一个进程中的线程共享相同的内存单元/内存地址空间->可以访问相同的变量和对象,而且它们从同一堆中分配对象->通信、数据交换、同步操作(所以线程容易产生并发问题)
  • 由于线程间的通信在同一地址空间上进行的,所以不需要额外的通信机制,这就使得通信更简便而且信息传递的速度也更快

线程的实现

继承Thread+run()

启动:创建子类对象+对象.start()
步骤:

  1. 创建多线程 继承Thread +重写run(线程体)
  2. 使用线程,创建子类对象 + 对象.start() 线程启动
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
/**
* 〈模拟龟兔赛跑〉
* 1、创建多线程 继承Thread +重写run(线程体)
* 2、使用线程,创建子类对象 + 对象.start() 线程启动
* @author Zephon
*/
public class Rabbit extends Thread {
@Override
public void run() {
//线程体
for(int i=0;i<100;i++){
System.out.println("兔子跑了"+i+"步");
}
}

public static void main(String[] args){
Rabbit rabbit = new Rabbit();
Tortoise tortoise = new Tortoise();

rabbit.start();//不要调用run方法 start方法中有一个start0方法,其中会调用run()方法,可以查看API
tortoise.start();

for(int i=0;i<1000;i++){
System.out.println("main==>"+i);
}
}
}
class Tortoise extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("乌龟跑了"+i+"步");
}
}
}
  • 在Java中负责线程功能的是Java.lang.Thread类
  • 可以通过创建Thread的实例来创建新的线程
  • 每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作,方法run()称为线程体
  • 通过调用Thread类的start()方法来启动一个线程

实现Runnable+run() (推荐)

  此处用到了一个静态代理的原理。
  静态代理模式,简而言之就是有一个真实角色,一个代理角色,且二者实现相同的接口,而代理角色则是持有真实角色的引用。

启动:使用静态代理
步骤:

  1. 创建真实角色
  2. 创建代理角色 Thread+引用
  3. 代理角色.start()
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

/**
* 〈使用Runnable创建线程〉
* 1、类 实现Runnable接口+重写run() -->真实角色类
* 2、启动多线程 使用静态代理
* 1)创建真实角色
* 2)创建代理角色+真实角色比引用
* 3)调用.start() 启动线程
*
* @author Zephon
*/
public class Programmer implements Runnable {
@Override
public void run() {
for(int i=0;i<1000;i++){
System.out.println("一边敲代码。。。");
}
}
public static void main(String[] args){
//创建真实角色
Programmer programmer = new Programmer();
//创建代理角色
Thread proxy = new Thread(programmer);
//调用.start()启动线程
proxy.start();

for(int i=0;i<1000;i++){
System.out.println("一边看小说...");
}
}
}
  • 继承Thread类方式的缺点:如果我们的类已经从一个类继承,则无法继承Thread类
  • 通过Runnable接口实现多线程
  • 优点:可以同时实现继承。实现Runnable接口方式要通用一些(避免单继承的局限性;便于共享资源,同一份资源,多个代理访问)

通过Callable接口实现多线程 (了解)

  • 优点:可以获取返回值,可以声明异常
  • 缺点:繁琐

步骤:

  1. 创建Callable实现类+重写call
  2. 借助 执行调度服务ExecutorService,获取Future对象
  3. 获取值result.get()
  4. 停止服务set.shutdownNow()
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
/**
* 〈使用Callable创建线程〉
* 1. 创建Callable实现类+重写call
* 2. 借助 执行调度服务ExecutorService,获取Future对象
* 3. 获取值result.get()
* 4. 停止服务set.shutdownNow()
* @author Zephon
*/
public class Call {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程
ExecutorService ser = Executors.newFixedThreadPool(2);
Race tortoise = new Race("乌龟",150);
Race rabbit = new Race("兔子",50);
//获取值
Future<Integer> result1 = ser.submit(tortoise);
Future<Integer> result2 = ser.submit(rabbit);

Thread.sleep(1000);
tortoise.setFlag(false);
rabbit.setFlag(false);


int num1 = result1.get();
int num2 = result2.get();
System.out.println("乌龟跑了-->"+num1+"步");
System.out.println("兔子跑了-->"+num2+"步");
//停止服务
ser.shutdownNow();
}

}
class Race implements Callable<Integer>{
private String name;//名称
private long time; //延时
private boolean flag = true;
private int step = 0;//步
public Race(){}

public Race(String name,long time){
super();
this.name = name;
this.time = time;
}

public void setFlag(boolean flag) {
this.flag = flag;
}

@Override
public Integer call() throws Exception {
while(flag){
Thread.sleep(time);//相当于延时
step++;
}
return step;
}

}