您的位置:首页 > 编程语言 > Java开发

java多线程的基础知识总结(附代码)

2019-04-29 17:03 337 查看

前言

本篇文章给大家带来的内容是关于java多线程的基础知识总结(附代码与资料),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

Java 主线程名

我们启动的一个程序可以理解为一个进程, 一个进程中包含一个主线程, 线程可以理解为一个子任务. Java 中可以通过下面代码来获取默认的主线程名.

1

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

运行结果为 main, 这是线程的名字并不是 main 方法, 通过此线程来执行 main 方法而已.

两种方式创建线程

1.继承 Thread 类

1

2

3

4

5

6

public
 
class
 
Thread1 
extends
 
Thread {

    
@Override

    
public
 
void run() {

        
System.out.println(
"qwe"
);

    
}

}

2.实现 Runnable 接口

1

2

3

4

5

6

public
 
class
 
Thread2 
implements
 
Runnable {

    
@Override

    
public
 
void run() {

        
System.out.println(
"asd"
);

    
}

}

Thread 实现 Runnable 接口. 并且多线程运行时, 代码的执行顺序与调用顺序是无关的. 另外如果多次调用 start方法则会抛出 java.lang.IllegalThreadStateException

currentThread 方法

返回当前代码正在被哪个线程调用.

1

2

3

4

5

6

7

8

9

10

11

public
 
class
 
Thread1 
extends
 
Thread {

 

    
public
 
Thread1() {

        
System.out.println(
"构造方法的打印:"
 
+ Thread.currentThread().getName());

    
}

 

    
@Override

    
public
 
void run() {

        
System.out.println(
"run 方法的打印:"
 
+ Thread.currentThread().getName());

    
}

}

1

2

Thread1 thread1 = 
new
 
Thread1();

thread1.start();

为什么某些人会一直比你优秀,是因为他本身就很优秀还一直在持续努力变得更优秀,而你是不是还在满足于现状内心在窃喜! 关注我,获取免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构视频学习资料以及电子书资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

欢迎学习Java的朋友们加入Java高级进阶:524903398,Q群内已经上传了一系列的面试答案以及架构学习资料,同时工作以及项目上遇到的问题都可以在群内提出。

isAlive 方法

判断当前线程是否处于活动状态.

1

2

3

4

5

6

7

8

public
 
class
 
Thread1 
extends
 
Thread {

    
@Override

    
public
 
void run() {

        
System.out.println(
"run 方法的打印 Thread.currentThread().isAlive() == "
 
+ Thread.currentThread().isAlive());

        
System.out.println(
"run 方法的打印 this.isAlive() == "
 
+ this.isAlive());

        
System.out.println(
"run 方法的打印 Thread.currentThread().isAlive() == this.isAlive() == "
 
+ (Thread.currentThread() == this ? 
"true"
 
"false"
));

    
}

}

1

2

3

4

5

6

Thread1 thread1 = 
new
 
Thread1();

 

System.out.println(
"begin == "
 
+ thread1.isAlive());

thread1.start();

Thread.sleep(1000);

System.out.println(
"end == "
 
+ thread1.isAlive());

执行结果如下

1

2

3

4

5

begin == false

run 方法的打印 Thread.currentThread().isAlive() == true

run 方法的打印 this.isAlive() == true

run 方法的打印 Thread.currentThread() == this == true

end
 
== false

thread1 在 1秒之后执行完成. 所以输出结果为 false. 并且 Thread.currentThread() 和 this 是同一个对象, 可以理解成执行当前 run 方法的线程对象就是我们自己(this).

如果将线程对象以构造参数的方式传递给 Thread 对象进行 start() 启动时, 运行结果和前面实例是有差异的. 造成这样的差异的原因还是来自于 Thread.currentThread() 和 this 的差异.

1

2

3

4

5

6

7

8

System.out.println(
"begin == "
 
+ thread1.isAlive());

//thread1.start();

// 如果将线程对象以构造参数的方式传递给 Thread 对象进行 start() 启动

Thread thread = 
new
 
Thread(thread1);

thread.start();

 

Thread.sleep(1000);

System.out.println(
"end == "
 
+ thread1.isAlive());

执行结果

1

2

3

4

5

begin == false

run 方法的打印 Thread.currentThread().isAlive() == true

run 方法的打印 this.isAlive() == false

run 方法的打印 Thread.currentThread() == this == false

end
 
== false

Thread.currentThread().isAlive() 是 true 的原因是因为, Thread.currentThread() 返回的是 thread 对象, 而我们也是通过此对象来启动线程的, 所以是在活动状态.

this.isAlive() 是 false 就比较好理解了, 因为我们是通过 thread 对象启动的线程来执行 run 方法的. 所以它是 false. 同时也说明这两个不是同一个对象.

sleep 方法

在指定的毫秒数内让当前 "正在执行的线程" 休眠. "正在执行的线程" 只得是 

Thread.currentThread()
 返回的线程.

1

Thread.sleep(1000);

停止线程

停止一个线程意味着在线程处理完任务之前停掉正在做的操作, 也就是放弃当前的操作.

在 Java 中有以下 3 种方法可以终止正在运行的线程:

  1. 使用退出标志, 使线程正常退出, 也就是当 run 方法完成后线程终止.

  2. 使用 stop 方法强行终止线程, 但是不推荐使用这个方法.

  3. 使用 interrupt 方法中断线程.

停不了的线程

调用 interrupt 方法仅仅是当前线程打了一个停止标记, 并不是真正的停止线程.

1

2

3

4

5

6

7

8

9

public
 
class
 
Thread1 
extends
 
Thread {

    
@Override

    
public
 
void run() {

 

        
for
(int i = 0; i < 500000; i++) {

            
System.out.println(i);

        
}

    
}

}

1

2

3

4

Thread1 thread1 = 
new
 
Thread1();

thread1.start();

Thread.sleep(2000);

thread1.interrupt();

我们两秒后调用 interrupt 方法, 根据打印结果我们可以看到线程并没有停止, 而是在执行完 500000 此循环后 run 方法结束线程停止.

判断线程是否是停止状态

我们将线程标记为停止后, 需要在线程内部判断一下这个线程是否是停止标记, 如果是则停止线程.

两种判断方法:

  1. Thread.interrupted(); 也可以使用 this.interrupted();

  2. this.isInterrupted();

下面是两个方法的源码:

1

2

3

4

5

6

7

public
 
static
 
boolean interrupted() {

    
return
 
currentThread().isInterrupted(true);

}

 

public
 
boolean isInterrupted() {

    
return
 
isInterrupted(false);

}

interrupted()
 方法数据静态方法, 也就是判断当前线程是否已经中断. 
isInterrupted()
 判断线程是否已经被中断.

来自官网的 
interrupted()
 方法重点. 
线程的 中断状态 由该方法清除. 换句话说, 如果连续两次调用该方法, 则第二次调用将返回 false (在第一次调用已清除了其中断状态之后, 且第二次调用检验完中断状态前, 当前线程再次中断的情况除外).

异常停止线程

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

public
 
class
 
Thread1 
extends
 
Thread {

    
@Override

    
public
 
void run() {

        
try
 
{

            
for
 
(int i = 0; i < 500000; i++) {

                
if
 
(this.isInterrupted()) {

                    
System.out.println(
"线程停止"
);

                    
throw
 
new
 
InterruptedException();

                
}

 

                
System.out.println(
"i = "
 
+ i);

            
}

        
catch
 
(InterruptedException e) {

            
System.out.println(
"线程通过 catch 停止"
);

            
e.printStackTrace();

        
}

    
}

}

1

2

3

4

Thread1 thread1 = 
new
 
Thread1();

thread1.start();

Thread.sleep(1000);

thread1.interrupt();

输出结果, 这是最后几行:

1

2

3

4

5

6

7

i = 195173

i = 195174

i = 195175

线程停止

线程通过 
catch
 
停止

java.lang.InterruptedException

    
at Thread1.run(Thread1.java:9)

当然我们也可以将 
throw new InterruptedException();
 换成 
return
. 都是一样可以结束线程的.

在沉睡中停止

如果线程调用 

Thread.sleep()
 方法使线程进行休眠, 这时我们调用 
thread1.interrupt()
后会抛出. 
InterruptedException
 异常.

1

2

3

java.lang.InterruptedException: sleep interrupted

    
at java.lang.Thread.sleep(Native Method)

    
at Thread1.run(Thread1.java:8)

暴力停止线程

暴力停止线程可以使用 

stop
 方法, 但此方法已经过时并不推荐使用, 原因如下.

  1. 即刻抛出 ThreadDeath 异常, 在线程的run()方法内, 任何一点都有可能抛出ThreadDeath Error, 包括在 catch 或 finally 语句中. 也就是说代码不确定执行到哪一步就会抛出异常.

  2. 释放该线程所持有的所有的锁. 这可能会导致数据不一致性.

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

public
 
class
 
Thread1 
extends
 
Thread {

 

    
private
 
String userName = 
"a"
;

    
private
 
String pwd = 
"aa"
;

 

    
public
 
String getUserName() {

        
return
 
userName;

    
}

 

    
public
 
void setUserName(String userName) {

        
this.userName = userName;

    
}

 

    
public
 
String getPwd() {

        
return
 
pwd;

    
}

 

    
public
 
void setPwd(String pwd) {

        
this.pwd = pwd;

    
}

 

    
@Override

    
public
 
void run() {

        
this.userName = 
"b"
;

        
try
 
{

            
Thread.sleep(100000);

        
catch
 
(InterruptedException e) {

            
e.printStackTrace();

        
}

        
this.pwd = 
"bb"
;

 

    
}

}

1

2

3

4

5

6

Thread1 thread1 = 
new
 
Thread1();

thread1.start();

Thread.sleep(1000);

thread1.stop();

 

System.out.println(thread1.getUserName() + 
"     "
 
+ thread1.getPwd());

输出结果为:

1

b     aa

  • 我们在代码中然线程休眠 Thread.sleep(1000 1d25c 00); 是为了模拟一些其它业务逻辑处理所用的时间, 在线程处理其它业务的时候, 我们调用 stop 方法来停止线程.
  • 线程是被停止了也执行了 System.out.println(thread1.getUserName() + " " + thread1.getPwd()); 来帮我们输出结果, 但是 this.pwd = "bb"; 并没有被执行.
  • 所以, 当调用了 stop 方法后, 线程无论执行到哪段代码, 线程就会立即退出, 并且会抛出 ThreadDeath 异常, 而且会释放所有锁, 从而导致数据不一致的情况.
  • interrupt 相比 stop 方法更可控, 而且可以保持数据一致, 当你的代码逻辑执行完一次, 下一次执行的时候, 才会去判断并退出线程.
  • 如果大家不怎么理解推荐查看 为什么不能使用Thread.stop()方法? 这篇文章. 下面是另一个比较好的例子.
  • 如果线程当前正持有锁(此线程可以执行代码), stop之后则会释放该锁. 由于此错误可能出现在很多地方, 那么这就让编程人员防不胜防, 极易造成对象状态的不一致. 例如, 对象 obj 中存放着一个范围值: 最小值low, 最大值high, 且low不得大于high, 这种关系由锁lock保护, 以避免并发时产生竞态条件而导致该关系失效.
  • 假设当前low值是5, high值是10, 当线程t获取lock后, 将low值更新为了15, 此时被stop了, 真是糟糕, 如果没有捕获住stop导致的Error, low的值就为15, high还是10, 这导致它们之间的小于关系得不到保证, 也就是对象状态被破坏了!
  • 如果在给low赋值的时候catch住stop导致的Error则可能使后面high变量的赋值继续, 但是谁也不知道Error会在哪条语句抛出, 如果对象状态之间的关系更复杂呢?这种方式几乎是无法维护的, 太复杂了!如果是中断操作, 它决计不会在执行low赋值的时候抛出错误, 这样程序对于对象状态一致性就是可控的.

suspend 与 resume 方法

用来暂停和恢复线程.

独占

1

2

3

4

5

6

7

8

9

10

11

12

13

public
 
class
 
PrintObject {

    
synchronized 
public
 
void printString(){

        
System.out.println(
"begin"
);

        
if
(Thread.currentThread().getName().equals(
"a"
)){

            
System.out.println(
"线程 a 被中断"
);

            
Thread.currentThread().suspend();

        
}

        
if
(Thread.currentThread().getName().equals(
"b"
)){

            
System.out.println(
"线程 b 运行"
);

        
}

        
System.out.println(
"end"
);

    
}

}

1

2

3

4

5

6

7

8

9

10

11

try
{

    
PrintObject  pb = 
new
 
PrintObject();

    
Thread thread1 = 
new
 
Thread(pb::printString);

    
thread1.setName(
"a"
);

    
thread1.start();

    
thread1.sleep(1000);

 

    
Thread thread2 = 
new
 
Thread(pb::printString);

    
thread2.setName(
"b"
);

    
thread2.start();

}
catch
(InterruptedException e){ }

输出结果:

1

2

begin

线程 a 被中断

当调用 Thread.currentThread().suspend(); 方法来暂停线程时, 锁并不会被释放, 所以造成了同步对象的独占.

以上就是java多线程的基础知识总结(附代码)的详细内容,更多请关注我要学编程网wyxbc.com其它相关文章!

 

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: