Java多线程基础

Posted bopo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java多线程基础相关的知识,希望对你有一定的参考价值。

一、线程概述

1. 并发与并行

并行:指在同一时刻,有多条指令在多个处理器上同时执行;

并发:指在同一时刻只能有一条指令执行,但是被快速轮转,表现为多线程。

2. 多线程编程的优点

  • 进程之间不能共享内存,但是线程之间共享内存很容易;
  • 对操作系统而言,线程的创建代价小,而进程的创建和销毁代价很高;

二、线程创建和启动

1. 继承Thread类创建线程类

  1. 定义Thead类的子类,重写run()方法;
  2. 创建Thead子类的实例,即创建了线程对象;
  3. 调用线程对象的start()方法来启动线程;
技术分享图片
 1 package thread;
 2 
 3 public class FirstThread extends Thread{
 4 
 5     private int i = 0;
 6     @Override
 7     public void run() {
 8         
 9         for(;i<100;i++){
10             System.out.println(getName() + " " + i);
11         }
12     }
13     
14     public static void main(String[] args) {
15         for(int i=0;i<100;i++){
16             System.out.println(Thread.currentThread().getName() +  " " + i);
17             
18             if(i==20){
19                 new FirstThread().start();
20                 new FirstThread().start();
21             }
22         }
23     }
24 }
View Code

 

  • Thread.currentThread(),返回当前正在执行的线程对象;
  • getName(),返回调用该方法的线程名字;
  • 多个线程中间无法共享线程类的实例变量;

2. 实现Runable接口创建线程类

  1. 定义Runable接口的实现类,重写run方法;
  2. 创建Runable实现类的实例,并以此实例作为Thread的target来创建Thread对象;
  3. 调用线程对象的start方法启动线程;
技术分享图片
 1 package thread;
 2 
 3 public class SecondThread implements Runnable{
 4 
 5     private int i = 0;
 6     
 7     @Override
 8     public void run() {
 9         for(;i<100;i++){
10             System.out.println(Thread.currentThread().getName() + " " + i);
11         }
12     }
13     
14     public static void main(String[] args) {
15         
16         for(int i = 0;i<100;i++){
17             System.out.println(Thread.currentThread().getName() + " " + i);
18         
19             if( i == 20){
20                 SecondThread st = new SecondThread();
21                 new Thread(st,"新线程1").start();
22                 new Thread(st,"新线程2").start();
23             }
24         }
25         
26     }
27 }
View Code

 

  • 由于实现Runable接口创建的线程,多个线程共享一个target类时,可以共享实例变量。

3. 使用Callable和Future创建线程

Callable接口是Runable接口的增强版,提供了一个call()方法作为线程执行体,但是比run方法更强大!

  1. call方法可以有返回值
  2. call方法可以声明抛出异常

创建并启动有返回值的线程的步骤:

  1. 创建Callable接口的实现类,并实现call方法;
  2. 使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call方法的返回值;
  3. 使用FutureTask对象作为Thread对象的target创建并启动新线程;
  4. 调用FutureTask对象的get方法来获取子线程执行结束后的返回值;

FutureTask类的公共方法:

  • boolean cancel(boolean mayInterruptIfRunning)
  • V get()
  • V get(long timeout, TimeUnit unit)
  • boolean isCancelled()
  • boolean isDone()
技术分享图片
 1 package thread;
 2 
 3 import java.util.concurrent.Callable;
 4 import java.util.concurrent.FutureTask;
 5 
 6 public class ThirdThread {
 7 
 8     public static void main(String[] args) {
 9         
10         ThirdThread rt = new ThirdThread();
11         
12         FutureTask<Integer> task = new FutureTask<>(
13                 (Callable<Integer>) () -> {
14                     int i=0;
15                     for(;i<100;i++){
16                         System.out.println(Thread.currentThread().getName()
17                                 + " 的循环变量i的值: " + i);
18                     }
19                     return i;
20                 }
21         );
22         
23         for(int i=0;i<100;i++){
24             System.out.println(Thread.currentThread().getName()
25                     + " 的循环变量i的值: " + i );
26             if( i== 20){
27                 new Thread(task, "有返回值的线程").start();
28             }
29         }
30         
31         try{
32             System.out.println("子线程的返回值" + task.get());
33         }catch (Exception e) {
34             e.printStackTrace();
35         }
36      }
37 }
View Code

 三、线程的生命周期

Java线程的五种状态:新建,就绪,运行,阻塞,死亡。

技术分享图片

1. 新建和就绪状态

new创建一个线程之后,线程就处于新建状态。

调用线程的start()方法之后,线程处于就绪状态。

注意:只能对新建的线程调用start()方法,否则引入IllegalThreadStateException异常。

2. 运行和阻塞状态

线程从运行状态进入阻塞状态:

  1. 线程调用sleep方法主动放弃所占用的处理器资源
  2. 线程调用了一个阻塞IO,等待IO结果
  3. 线程等待一个排它锁
  4. 线程等待一个通知
  5. 调用线程的suspend方法,主动挂起线程。

线程从阻塞状态进入就绪状态:

  1. sleep方法经过了指定的时间;
  2. 线程IO结果返回;
  3. 线程获取了锁;
  4. 线程收到了通知;
  5. 挂起的线程被调用了resume方法恢复;

3. 线程死亡

有三种方式结束线程:

  1. run和call方法执行完成,线程正常结束;
  2. 线程抛出了一个未捕获的异常;
  3. 直接调用线程的stop方法结束线程(容易死锁);

四、控制线程

1. join线程

一个线程通过调用另一个线程的join方法,等待另外一个线程完成后,继续执行。

join()方法的三种重载:

  1. join()
  2. join(long millis)
  3. join(long millis, int nanos):等待最长milis毫秒加nanos毫微秒
技术分享图片
 1 package thread;
 2 
 3 public class JoinThread extends Thread{
 4 
 5     // 提供带参构造函数,为线程起名
 6     public JoinThread(String name) {
 7         super(name);
 8     }
 9     
10     @Override
11     public void run() {
12         
13         for(int i=0;i<100;i++) {
14             System.out.println(getName() + " " + i);
15         }
16     }
17     
18     public static void main(String[] args) throws Exception {
19         new JoinThread("新线程").start();
20         
21         for(int i=0;i<100;i++) {
22             if(i==20) {
23                 JoinThread jt = new JoinThread("被Join的线程");
24                 jt.start();
25                 //main线程调用了join方法,必须等待jt执行结束,才能继续执行
26                 jt.join();
27             }
28             System.out.println(Thread.currentThread().getName()
29                     + " " + i );
30         }
31     }
32 }
View Code

 

2. 后台线程

后台线程的特征:如果所有的前台线程全部死亡,后台线程自动死亡。

调用thread对象的setDeamon(True)方法,将线程设置为后台线程。

技术分享图片
 1 package thread;
 2 
 3 public class DeamonThread  extends Thread{
 4 
 5     @Override
 6     public void run() {
 7         
 8         for(int i=0;i<1000;i++) {
 9             System.out.println(getName() + " " + i);
10         }
11     }
12     
13     public static void main(String[] args) {
14         
15         DeamonThread t = new DeamonThread();
16         t.setDaemon(true);
17         t.start();
18         for(int i=0;i<10;i++) {
19             System.out.println(Thread.currentThread().getName()
20                     + " " + i );
21         }
22     }
23 }
View Code

 

3. 线程睡眠:sleep

让正在运行的线程暂停一段时间,进入阻塞状态,需要调用Thread类的sleep方法来实现!

两种重载形式:

  1. sleep(long millis)
  2. sleep(long millis, int nanos)
技术分享图片
 1 package thread;
 2 
 3 import java.util.Date;
 4 
 5 public class SleepTest {
 6 
 7     public static void main(String[] args) throws InterruptedException {
 8         for(int i=0;i<10;i++) {
 9             System.out.println("当前时间 :" + new Date());
10             Thread.sleep(1000);
11         }
12     }
13 }
View Code

 

4. 线程让步:yield

yield方法是和sleep方法很类似的一个Thread类中的静态方法,作用是让正在进行的线程暂停,直接转为就绪状态,等待下次调度。

需要注意的是,当某个线程调用了yield方法之后,只有优先级与当前线程相同或者更高并处于就绪状态的线程才能获得执行机会。

 

5. 改变线程优先级

五、线程同步

六、线程通信

七、线程相关类

 

以上是关于Java多线程基础的主要内容,如果未能解决你的问题,请参考以下文章

java基础入门-多线程同步浅析-以银行转账为样例

java多线程基础

Java基础之多线程

Java多线程基础

多线程编程学习一(Java多线程的基础)

Java 多线程基础多线程的实现方式