多线程(一) 基础

一、定义

程序:是为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态代码,对象。

进程:是程序执行一次的过程,正在运行的一个程序。动态过程:有他自身产生,存在,消亡的过程

线程:程序内部的一条执行入径。

二、优点

背景:只使用单个线程完成多个任务(调用多个方法),肯定比用多个线程来完成用的时间更短,为何仍需多线程呢?

优点:

  1. 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
  2. 提高计算机系统CPU的利用率
  3. 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改

三、线程的创建方式

3.1 继承Thread重写run()方法

1
2
3
4
5
class A extends Thread{
@Override
public void run(){
}
}

3.2 实现Runnable接口的run方法,推荐

不影响类的继承。因为类是单继承的。

针对于有共享数据的操作,更适合使用Runnable的方式。换句话说,实现Runnable接口的方式,实现了代码和数据的分离。

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
39
/**
* @author xia17
* @date 2020/1/2 20:16
*/
public class TestA {

@Test
public void run() throws InterruptedException {
//1、
new Thread(new A()).start();
//2、匿名内部类
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
};
new Thread(runnable).start();
//3、lambda 表达式
Runnable runnable1 = ()-> System.out.println("hello");
new Thread(runnable1);
//4、lambda 表达式
new Thread(()-> System.out.println("")).start();
//5、lambda 表达式
new Thread(B::run).start();
}
}

class A implements Runnable{
@Override
public void run(){
}
}

class B {
public static void run(){
System.out.println("da");
}
}

3.3 通过线程池中获取 这种也是推荐的

实现 ThreadFactory 接口,实现 newThread 方法

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
/**
* 线程池
* @author xia17
* @date 2020/1/7 20:54
*/
public class NameThreadFactory implements ThreadFactory {
private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;

private NameThreadFactory() {
this("default-name-pool");
}

private NameThreadFactory(String name){
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
//此时namePrefix就是 name + 第几个用这个工厂创建线程池的
this.namePrefix = name +
POOL_NUMBER.getAndIncrement();
}

@Override
public Thread newThread(Runnable r) {
//此时线程的名字 就是 namePrefix + -thread- + 这个线程池中第几个执行的线程
Thread t = new Thread(group, r,
namePrefix + "-thread-"+threadNumber.getAndIncrement(),
0);
if (t.isDaemon()){
t.setDaemon(false);
}
if (t.getPriority() != Thread.NORM_PRIORITY){
t.setPriority(Thread.NORM_PRIORITY);
}
return t;
}

public static NameThreadFactory build(){
return new NameThreadFactory();
}

public static NameThreadFactory build(String name){
return new NameThreadFactory(name);
}



}

四、Thread类的常用方法

  • run():Thread的子类一定要重写的方法。将此分线程要执行的操作,声明在run()中
  • start():要想启动一个分线程,就需要调用start():①启动线程②调用线程的run()
  • currentThread():静态方法,获取当前的线程
  • getName():获取当前线程的名字
  • setName(String name):设置当前线程的名字
  • yield():当前线程调用此方法,释放CPU的执行权
  • join():在线程a中调用线程b的join()方法:只用当线程b执行结束以后,线程a结束阻塞状态,继续执行。
  • sleep(long millitimes):让当前的线程睡眠millitimes毫秒
  • isAlive():判断当前线程是否存活

五、线程的生命周期

  1. 新生态:刚创建

  2. 就绪态:调用了start

  3. 运行态: 抢到了Cpu执行时间

  4. 阻塞态:线程失去了Cpu执行时间

    1. 等待队列:调用了wait()方法
    2. 锁池:等待锁标记
  5. 死亡态:线程执行完毕

img

六、线程的优先级

线程调用 setPriority(int i) 设置优先级。

设置线程的优先级只是修改这个线程可以抢到Cpu执行时间的一个概率,并不能保证优先级高的一定比优先级低的先执行。

优先级的设置是一个整数,【0,10】区间内,默认是5

优先级的设置必须放到执行之前

七、线程的礼让

指的是让当前运行的线程释放自己的cpu资源,由运行状态回到就绪状态。

线程调用yield() 方法释放自己的cpu资源。

八、线程的分类

Java中的线程分为两类:一种是守护线程,一种是用户线程

8.1 守护线程

Java中的线程分为两类:一种是守护线程,一种是用户线程

  • 它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。
  • 守护线程是用来服务用户线程的,通过在start()方法前调用thread.setDaemon(true)可以把一个用户线程变成一个守护线程。
  • Java垃圾回收就是一个典型的守护线程
  • 若JVM中都是守护线程,当前JVM将退出