我是靠谱客的博主 尊敬乌龟,这篇文章主要介绍Java—多线程的实现及常用操作sleep、yield、join、线程停止方法详解,现在分享给大家,希望可以做个参考。

1.进程与线程

进程:指OS中一个程序的执行周期

线程:一个程序同时执行多个任务,其中每个任务都是一个线程

二者区别:每个进程拥有自己的一整套变量,线程则共享数据。共享变量是的线程之间通信比进程间的更有效、方便

2.线程的五种状态:

创建–>就绪–>阻塞–>运行–>终止

在这里插入图片描述

3.Java多线程

3.1通过继承Thread类覆写run()方法来实现多线程

启动多线程:public synchronized void start() ,用此方法来自动调用线程的run()方法

线程的核心类包: java.lang.Thread

复制代码
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 MyThread extends Thread{ //线程主体类 private String line; public MyThread (String line){ this.line = line; } @Override public void run(){ //覆写run()方法,线程从此开始执行 for(int i = 0; i<10; i++){ System.out.println(this.line+",i="+i); } } public static void main(String[] args) { MyThread myThread1 = new MyThread("thread1") ; MyThread myThread2 = new MyThread("thread2") ; MyThread myThread3 = new MyThread("thread3") ; //顺序执行,非多线程 myThread1.run(); myThread2.run(); myThread3.run(); //通过启动多线程:public synchronized void start()此方法来自动调用线程的run()方法 myThread1.start(); myThread2.start(); myThread3.start();//此时三个线程是同时、交替执行的 } }

3.2通过实现Runnable()接口

Thread类的构造方法:public Thread(Runnable target) ,用来接收Runnable接口对象

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//Runnable()接口 @FunctionalInterface public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }
复制代码
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
//利用Runnable接口实现多线程 class MyThread implements Runnable{ private String line; public MyThread(String line){ this.line = line; } @Override public void run(){ for(int i = 0; i<10 ;i++){ System.out.println(this.line+",i="+i); } } } public class TestDemo { public static void main(String[] args) { MyThread myThread1= new MyThread("Thread1"); MyThread myThread2= new MyThread("Thread2"); MyThread myThread3= new MyThread("Thread3"); new Thread(myThread1).start(); new Thread(myThread2).start(); new Thread(myThread3).start(); } } //利用Runnable接口可实现共享,实现一个简单的卖票流程 class MyThread1 implements Runnable{ private int tickets = 5; public void setTickets(int tickets){ this.tickets = tickets; } @Override public void run(){ while(tickets>0){ System.out.println(Thread.currentThread().getName()+"剩余票数:"+tickets--); } } } public class SellTickets { public static void main(String[] args) { MyThread1 myThread1 = new MyThread1(); MyThread1 myThread2 = new MyThread1(); MyThread1 myThread3 = new MyThread1(); new Thread(myThread1).start(); new Thread(myThread2).start(); new Thread(myThread3).start(); } }

Runnable接口实现多线程效果图:
在这里插入图片描述
卖票窗口效果图:
在这里插入图片描述

  • 通常采用匿名内部类Lambda表达式来创建Runnable对象
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//使用匿名内部类创建Runnable对象 public class Thread1 { public static void main(String[] args) { //Runnable runnable = new Runnable(); //Thread thread = new Thread(runnable,"子线程1"); //thread.start();----->new Thread (thread).start(); new Thread(new Runnable(){ @Override public void run(){ System.out.println("HelloWorld"); } }).start(); //注意!!! } } //使用Lamdba表达式创建Runnable对象 public class Thread1 { public static void main(String[] args) { Runnable runnable = () -> System.out.println("HelloWorld"); new Thread(runnable).start(); } }

3.3Thread类和Runnable区别

Thread:类,单继承,是Runnable接口的子类,覆写了Runnable接口的run()方法

Runnable:接口,可被多个类调用

4.Callable实现多线程

Runnable中的run()方法无返回值,用Callnable来实现带有返回值的多线程

Callable接口存在于java.util.concurrent包中

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
//Callable接口 @FunctionalInterface public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//使用Callable来定义卖票窗口 class MyThread2 implements Callable<String> { private int tickets = 10; public void setTickets(int tickets){ this.tickets = tickets; } @Override public String call() throws Exception { while(this.tickets>0){ System.out.println(Thread.currentThread().getName()+"剩余票数:"+this.tickets--); } return "票卖完啦!欢迎下次光临"; } }

Callable实现卖票窗口效果图:
在这里插入图片描述

  • 无论何种情况,启动线程只能调用Thread类中的start()方法。观察下面Callable继承树:在这里插入图片描述
    注意Thread和FutureTask两大板块!
复制代码
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
//启动并获得多线程的执行结果 public class SellTickets { public static void main(String[] args)throws ExecutionException, InterruptedException { //1.实例化多线程对象 MyThread2 myThread1 = new MyThread2(); MyThread2 myThread2 = new MyThread2(); MyThread2 myThread3 = new MyThread2(); //注意!!2.通过FutureTask类来包装Callable对象 FutureTask<String> task1 = new FutureTask<>(new MyThread2()); FutureTask<String> task2 = new FutureTask<>(new MyThread2()); FutureTask<String> task3 = new FutureTask<>(new MyThread2()); //3.启动线程 new Thread(task1).start(); new Thread(task2).start(); new Thread(task3).start(); //4.获取多线程的执行结果 System.out.println(task1.get()); /*在调用task.get()方法时抛出异常ExecutionException, InterruptedException*/ } }

注意

  1. Future代表一个异步的计算,可以从中得到计算结果,查看计算状态,它实现FutureTask可以被提交给Executor执行,多个线程能从中得到计算结果
  2. Callable和Future要配合使用,Future给出的get结果有两种状态:当结果还未被计算出来,线程被挂起,FutureTak内部使用一个单链表维持等待的线程;当结果已被计算出来,解除线程挂起,得到计算结果
  3. Future接口:代表一个异步的计算结果,接口中的方法用来检查计算是否完成、等待完成以及得到计算结果。其中get()方法得到结果
  4. FutureTask类:提供异步计算的结果的任务。它实现了RunnableFuture接口,而RunnableFuture接口又继承了Runnable和Future接口。所以FutureTask可以用来包装Callable或者Runnbale对象

4.多线程的常用操作方法

1.线程的命名和获取

复制代码
1
2
3
4
5
6
7
//命名线程 MyThread myThread = new MyThread(); new Thread(myThread,"子线程A").start(); //获取线程名 Thread.currentThread().getName();

主方法本身就是一个线程,所有的线程都是通过主线程创建并启动的

2.线程休眠sleep()

线程休眠:指是让线程暂缓执行一下,等到了预计时间之后再恢复执行

线程休眠时会交出CPU,让CPU去执行其他任务,但是sleep不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象

调用sleep()会抛出异常: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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//处理线程休眠操作 class MyThread3 implements Runnable{ private boolean flag = true; public void setFlag(boolean flag){ this.flag = flag; } @Override public void run(){ int i = 0; while(this.flag){ System.out.println("线程"+Thread.currentThread().getName()+"当前执行次数i:"+ ++i +"次"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class SleepThread { public static void main(String[] args) { MyThread3 mt1 = new MyThread3(); MyThread3 mt2 = new MyThread3(); MyThread3 mt3 = new MyThread3(); new Thread(mt1,"子线程A").start(); new Thread(mt2,"子线程B").start(); new Thread(mt3,"子线程C").start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } /* 注意!此处三个线程并非同时休眠,所有代码一次进入run()方法中,真正进入到方法的对象可能是多个,也可能是一个。进入代码的顺序可能有差异,但是总体的执行是并发执行。 */

效果图:
在这里插入图片描述
3.线程让步yield()

线程让步是指暂停当前正在执行的线程对象,并执行其他线程

yield()与sleep()相似,但yield不能控制交出CPU的具体时间且只能让拥有相同优先级的线程有获取CPU执行时间的机会

调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间

复制代码
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
//yield()下的线程休眠:休眠时间不确定,同一优先级获取CPU执行机会 class MyThread4 implements Runnable{ @Override public void run(){ for(int i = 0 ;i<5; i++){ Thread.yield(); System.out.println(Thread.currentThread().getName()+"循环执行第"+ ++i +"次"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class YieldThread { public static void main(String[] args) { MyThread4 mt1 = new MyThread4(); MyThread4 mt2 = new MyThread4(); MyThread4 mt3 = new MyThread4(); new Thread(mt1,"子线程A").start(); new Thread(mt2,"子线程B").start(); new Thread(mt3,"子线程C").start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }

效果图:
在这里插入图片描述
4.join()方法

join():等待该线程终止。如果在主线程中调用就会让主线程休眠,让调用该方法的线程run方法先执行后再开始执行主线程。

复制代码
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
//join()在线程中的使用方法--等待该线程停止 class MyThread5 implements Runnable{ @Override public void run(){ try { System.out.println("主线程睡眠前的时间:"); JoinThread.printTime(); Thread.sleep(2000); System.out.println(Thread.currentThread().getName()); System.out.println("睡眠结束的时间:"); JoinThread.printTime(); } catch (InterruptedException e) { e.printStackTrace(); } } } public class JoinThread { public static void main(String[] args) throws InterruptedException { MyThread5 mt1 = new MyThread5(); Thread thread = new Thread(mt1,"子线程A"); thread.start(); System.out.println(Thread.currentThread().getName()); thread.join(); System.out.println("代码结束"); } public static void printTime(){ Date date = new Date(); DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String time = format.format(date); System.out.println(time); } }

效果图:
在这里插入图片描述
5.线程停止

多线程中有三种方式可以停止线程:

  1. 设置标记位,可以使线程正常退出
  2. 使用stop方法强制使线程退出,但是该方法不太安全所以已经被废弃了
  3. 使用Thread类中的一个 interrupt() 可以中断线程
复制代码
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
/** *1.设置标记为使线程退出 **/ class MyThread6 implements Runnable { private boolean flag = true; @Override public void run() { int i = 1; while (flag) { try { Thread.sleep(2000); System.out.println("第" + i + "次执行,线程名称:" + Thread.currentThread().getName()); i++; } catch (InterruptedException e) { e.printStackTrace(); } } } public void setFlag(boolean flag) { this.flag = flag; } } public class StopThread { public static void main(String[] args) throws InterruptedException { MyThread6 mt1 = new MyThread6(); Thread thread = new Thread(mt1,"子线程A"); thread.start(); Thread.sleep(1000); mt1.setFlag(false); System.out.println("线程结束"); } }

效果图:
在这里插入图片描述

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/** *2.调用stop()方法使线程退出 **/ MyThread6 mt1 = new MyThread6(); Thread thread = new Thread(mt1,"子线程A"); thread.start(); Thread.sleep(1000); thread.stop(); System.out.println("线程结束"); /*为什么stop()不安全? 因为stop()方法会解除由线程获取的所有锁定,当调用此方法时,线程对象会立即停止正在执行的方法,若此方法为某种同步方法,如synchronized void { x = 3; y = 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
/** *3.使用Thread.interrupt()使线程退出 **/ class MyThread7 implements Runnable{ private boolean flag = true; @Override public void run(){ int i = 1; while(flag){ try { Thread.sleep(1000); boolean bool = Thread.currentThread().isInterrupted();//判断当前线程中断情况 if(bool){ System.out.println("线程处于非阻塞状态"+bool);//非阻塞状态 break; } System.out.println("第"+i+"次执行,线程名称:"+Thread.currentThread().getName()); /*阻塞状态,线程被调用了interrupt()方法,清除中断标志后抛出异常java.lang.InterruptedException*/ i++; } catch (InterruptedException e) { e.printStackTrace(); System.out.println("退出阻塞");//退出阻塞状态,且中断标志被系统自动清除,并将bool重新设置为false boolean bool = Thread.currentThread().isInterrupted(); System.out.println("阻塞中断:" + bool); return;//退出run()方法,中断进程 } } } public void setFlag(boolean flag){ this.flag = flag; } } public class InterruptThread { public static void main(String[] args) throws InterruptedException { MyThread7 mt1 = new MyThread7(); Thread thread = new Thread(mt1,"子线程A"); thread.start(); Thread.sleep(3000); thread.interrupt(); System.out.println("线程结束"); } }

效果图:
在这里插入图片描述

  • interrupte()方法并不会立即执行中断操作,而是给线程设置一个为true的中断标志(boolean型),根据线程状态进行不同操作
  • 如果线程状态为非阻塞,则仅仅是将线程的中断标志设为true
  • 阻塞状态:将中断标志设为true,如果是wait、sleep以及join三个方法引起的阻塞,则将线程的中断标志重新设置为false,并抛出InterruptedException;

完整代码移步:https://github.com/Loinbo

最后

以上就是尊敬乌龟最近收集整理的关于Java—多线程的实现及常用操作sleep、yield、join、线程停止方法详解的全部内容,更多相关Java—多线程内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(72)

评论列表共有 0 条评论

立即
投稿
返回
顶部