关于java多线程详细知识点

编程入门 行业动态 更新时间:2024-10-28 20:24:54

关于java多线程详细<a href=https://www.elefans.com/category/jswz/34/1770093.html style=知识点"/>

关于java多线程详细知识点

文章目录

  • 1.多线程概述
  • 2. 线程和进程的关系
  • 3.分析程序存在几个线程
  • 4. 实现线程的三种方式
  • 5.Thread和Runnable的区别
  • 6.线程的生命周期
  • 7.线程中的常用方法
  • 8.线程的sleep方法
  • 9. 终止线程睡眠
  • 10.关于synchronized关键字
  • 11.守护线程
  • 12.定时器作用和如何实现

1.多线程概述

什么是进程?什么是线程?

进程是一个应用程序(一个进程是一个软件)。

线程是一个进程中的执行单元或者执行场景。

2. 线程和进程的关系

举个例子:

阿里巴巴:进程A
马云:阿里巴巴的一个线程a
童文红:阿里巴巴的一个线程b

京东:进程B
刘强东:京东的一个线程a
奶茶妹妹:京东的一个线程b

进程A和进程B的内存独立不共享。
一个进程可以启动多个线程。

那么线程a和线程b呢?

在java语言中,线程a和线程b堆内存和方法区内存共享。
但是栈内存独立,一个线程一个栈。
假设启动十个线程,会有十个栈空间,每个栈与每个栈之间各自执行各的,互不干扰,这就是多线程并发。

思考一个问题:使用了多线程机制后,main方法结束,是不是整个程序也不会结束?

main方法结束,只是主栈空了,其他的栈(线程)可能还在压栈弹栈。

如图:

3.分析程序存在几个线程

分析下面代码有几个线程?

public class csdnTest {public static void main(String[] args) {System.out.println("main begin!");m1();System.out.println("main over!");
}private static void m1(){System.out.println("m1 begin!");m2();System.out.println("m1 over!");}private static void m2(){System.out.println("m2 begin!");m3();System.out.println("m2 over!");}private static void m3(){System.out.println("m3 begin!");System.out.println("m3 over!");}
}

答案是:一个。

4. 实现线程的三种方式

第一种方式:

/*
实现线程的第一种方式:编写一个类,继承Thread,重写run方法
*/
public class ThreadTest01 {public static void main(String[] args) {//创建一个分支栈的对象elseThread elseThread=new elseThread();//启动线程//start()方法的作用是:启动一个分支线程,在jvm中开辟一个新的栈空间,这段代码任务完成后就,瞬间就结束了//启动成功的线程会自动调用run()方法,并且run方法在分支栈的底部(压栈)//run方法在分支栈的底部,main方法在主栈的栈底部,run和main是平级的//elseThread.run(); 不会启动线程,不会分配新的分支栈(这种方式就是单线程)只是一个普通方法的调用elseThread.start();//这行代码不结束,下面的代码永远不会执行//这里的代码还是在主线程中for (int b=0;b<1000;b++){System.out.println("主线程————————"+b);}}
}
class elseThread extends Thread{public void run() {for (int a=0;a<1000;a++){System.out.println("支线程——————"+a);}}
}

运行结果:出现以下结果和线程抢占cpu时间片有关,主线程和支线程共同抢夺cpu时间片(也就是执行权),具体原理参考下面线程生命周期详解。

第二种方式:

/*
实现线程的第二种方式,编写一个类,实现Runnable接口,实现run方法,这种方式比较常用,因为一个类实现了一个接口,还可以继承其他的
类,但是一个类继承了一个类,就无法继承其他的类了
*/
public class ThreadTest02 {public static void main(String[] args) {s s=new s();Thread thread=new Thread(s);thread.start();for (int b=0;b<1000;b++){System.out.println("主线程————————"+b);}}
}
class s implements Runnable{@Overridepublic void run() {for (int a=0;a<1000;a++){System.out.println("支线程——————"+a);}}
}

运行结果:

第三种方式:

/*
采用匿名内部类的方法
*/
public class ThreadTest03 {public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {for (int a = 0; a < 1000; a++) {System.out.println("支线程——————" + a);}}});t.start();for (int b = 0; b < 1000; b++) {System.out.println("主线程————————" + b);}}
}

运行结果:

5.Thread和Runnable的区别

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

总结:

实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

6.线程的生命周期

线程生命周期存在五种状态:

1、新建状态(New):新创建了一个线程对象。

2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)

(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)

5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

如图:

7.线程中的常用方法

1.设置线程名字
2.获取线程名字
3.启动支线程

如下:

public class ThreadTest04 {public static void main(String[] args) {sss s=new sss();//支线程永远次于主线程执行*****s.start();//设置线程名字s.setName("s1");//输出线程名字System.out.println(s.getName());}
}
class sss extends Thread{@Overridepublic void run() {for (int a=0;a<1000;a++){System.out.println("支线程——————"+a);}}
}

运行结果:


4.获取当前线程对象

如下:

public class ThreadTest05 {public static void main(String[] args) {gxs gxs=new gxs();Thread thread=new Thread(gxs);thread.start();//获取当前线程对象 currentThread就是当前线程,当前线程也就是main方法主线程Thread currentThread=Thread.currentThread();thread.setName("支线程");//输出mainSystem.out.println(currentThread.getName());for (int b=0;b<1000;b++){System.out.println(currentThread.getName()+"————————"+b);}}
}
class gxs implements Runnable{@Overridepublic void run() {for (int a=0;a<1000;a++){Thread thread=Thread.currentThread();System.out.println(thread.getName()+"——————————"+a);}}
}

运行结果:

8.线程的sleep方法

Thread.sleep(long millis)

使当前线程进入停滞状态(阻塞当前线程),让出CUP的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会;

如下:

/*
关于线程sleep方法:
1.静态方法
2.参数是毫秒
3.作用:让当前线程进入休眠状态,进入阻塞状态,放弃占有cpu时间片,让给其他线程使用这段代码出现在a线程就会让a线程休眠这段代码出现在b线程就会让b线程休眠
*/
public class ThreadTest06 {public static void main(String[] args) {try {//让当前线程休眠五秒钟Thread.sleep(1000*5);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("睡醒了!");}
}

运行结果:程序等待五秒钟,然后输出“睡醒了!”

sleep方法可以模拟计时器:

//相当于是个定时器
public class ThreadTest07 {public static void main(String[] args) {for (int i=0;i<=10;i++){System.out.println(i);try {//每隔一秒钟输出一个数字Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

运行结果:每隔一秒输出一个数字

9. 终止线程睡眠

依靠java异常处理机制,中断当前线程睡眠,使用关键字interrupt。

public class ThreadTest08 {public static void main(String[] args) {Thread thread=new Thread(new teacher());thread.start();thread.setName("hahah");//模拟睡眠,希望线程睡眠五秒钟醒来try {Thread.sleep(1000*5);} catch (InterruptedException e) {throw new RuntimeException(e);//中断线程睡眠,这种中断睡眠的方式依靠了java异常处理机制}thread.interrupt();}
}
class teacher implements Runnable{//这里不能throws,因为子类重写,不能抛出比父类更多的异常//因为run方法的父类没有抛出任何异常.所以子类不能抛出异常@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"-------"+"begin");try {Thread.sleep(10000*60*60*24*365);} catch (InterruptedException e) {//throw语句一执行,后面的语句执行不到了throw new RuntimeException(e);}System.out.println("over");}
}

运行结果:支线程本应该睡眠一年,但是在主线程里面模拟线程睡眠五秒钟,然后通过java异常处理机制,抛出异常,终止线程睡眠。

10.关于synchronized关键字

synchronized关键字的作用:

在 Java 中,关键字 synchronized可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作),同时我们还应该注意到synchronized另外一个重要的作用,synchronized可保证一个线程的变化(主要是共享数据的变化)被其他线程所看到(保证可见性,完全可以替代Volatile功能),这点确实也是很重要的。

synchronized的三种应用方法:

1.修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁。
2.修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁,一个对象一把锁。
3.修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

第一种方式:修饰实例方法

/*
多线程代码现在的处理器一般都能解决,唯一需要解决的是线程安全*****
线程同步这一块,涉及两个专业术语:异步编程模型:异步就是并发同步编程模型:同步就是排队
*/
public class ThreadsafeTest10 {public static void main(String[] args) {//创建一个对象bank b=new bank("gxs",10000);//创建两个线程Thread t=new account(b);Thread t1=new account(b);//设置线程名字t.setName("t1");t1.setName("t2");//启动两个线程t.start();t1.start();}
}
class bank {String id;double balance;public bank() {}public bank(String id, double balance) {this.id = id;this.balance = balance;}public String getId() {return id;}public void setId(String id) {this.id = id;}public double getBalance() {return balance;}//定义一个模拟银行取款的方法public void setBalance(double balance) {this.balance = balance;}public synchronized void tikuan(double num) throws InterruptedException {//t1和t2线程并发,两个线程对应两个栈,操作同一个对象//synchronized () {线程同步代码块},处理线程同步机制语法,小括号里面填什么?填多个线程共享的对象,这里的共享对象是银行对象// Thread.sleep(1000);double before = this.balance;double after = before - num;//模拟发生网络延迟,余额更新慢延迟了try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}//更新余额this.setBalance(after);//System.out.println(after);}
}
class account extends Thread{private bank b;public account(bank b) {this.b=b;}@Overridepublic void run() {//模拟取款操作double money=5000;//调用取款方法try {b.tikuan(money);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread()+"对"+b.getId()+"取款成功"+"余额为"+b.getBalance());}
}

运行结果:使用synchronized关键字可以避免两个用户同时对同一种银行卡进行取款操作时,发生同时取走5000元,余额却还显示5000元。

第二种方式:修饰静态方法

public class test02 {public static void main(String[] args) throws InterruptedException {gxs g=new gxs();Thread t1=new mythread(g);Thread t2=new mythread(g);t1.setName("t1");t2.setName("t2");t1.start();Thread.sleep(1000);t2.start();}
}
class mythread extends Thread{@Overridepublic void run() {if (Thread.currentThread().getName().equals("t1")) {g.dosome();}if (Thread.currentThread().getName().equals("t2")) {g.doother();}}public gxs g;public mythread(gxs g) {this.g=g;}
}
class gxs{//这是类锁public synchronized static void dosome(){System.out.println("begin");try {Thread.sleep(1000*5);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("over");}//这是对象锁public synchronized static void doother(){System.out.println("过一会再输出吧");}
}

运行结果:先输出begin,过五秒几乎同时输出“over”和“过一会再输出吧”。注意:必须要对两个静态方法都要加上synchronized关键字,否则doother方法是不会等待t1线程结束再执行的。

第三种方式:修饰代码块

/*
多线程代码现在的处理器一般都能解决,唯一需要解决的是线程安全*****
线程同步这一块,涉及两个专业术语:异步编程模型:异步就是并发同步编程模型:同步就是排队
*/
public class ThreadsafeTest10 {public static void main(String[] args) {//创建一个对象bank b=new bank("gxs",10000);//创建两个线程Thread t=new account(b);Thread t1=new account(b);//设置线程名字t.setName("t1");t1.setName("t2");//启动两个线程t.start();t1.start();}
}
class bank {String id;double balance;public bank() {}public bank(String id, double balance) {this.id = id;this.balance = balance;}public String getId() {return id;}public void setId(String id) {this.id = id;}public double getBalance() {return balance;}//定义一个模拟银行取款的方法public void setBalance(double balance) {this.balance = balance;}public void tikuan(double num) throws InterruptedException {//t1和t2线程并发,两个线程对应两个栈,操作同一个对象//synchronized () {线程同步代码块},处理线程同步机制语法,小括号里面填什么?填多个线程共享的对象,这里的共享对象是银行对象// Thread.sleep(1000);synchronized (this) {double before = this.balance;double after = before - num;//模拟发生网络延迟,余额更新慢延迟了try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}//更新余额this.setBalance(after);//System.out.println(after);}}
}
class account extends Thread{private bank b;public account(bank b) {this.b=b;}@Overridepublic void run() {//模拟取款操作double money=5000;//调用取款方法try {b.tikuan(money);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread()+"对"+b.getId()+"取款成功"+"余额为"+b.getBalance());}
}

运行结果:

11.守护线程

概述:

java提供两种类型线程:一个是用户线程,一个是守护线程。
用户线程:是高优先级线程。JVM 会在终止之前等待任何用户线程完成其任务。
守护线程: 是低优先级线程。其唯一作用是为用户线程提供服务。

守护线程用来做什么?

常见的做法,就是将守护线程用于后台支持任务,比如垃圾回收、释放未使用对象的内存、从缓存中删除不需要的条目,按照这个解释,那么大多数 JVM 线程都是守护线程。

如何创建守护线程?

守护线程也是一个线程,因此它的创建和启动其实和普通线程没什么区别,要将普通线程设置为守护线程,方法很简单,只需要调用 Thread.setDaemon() 方法即可。

代码如下:旨在用户线程结束时,守护线程也跟着结束。

public class BakDataThreadTest {public static void main(String[] args) {//创建守护线程对象,父类型引用指向子类型对象Thread thread=new bakdata();//启动线程之前,将这个线程设置为守护线程thread.setDaemon(true);thread.start();thread.setName("守护线程");Thread current=Thread.currentThread();current.setName("主线程");//主线程:也就是用户线程for (int i=0;i<=10;i++){System.out.println(Thread.currentThread().getName()+"---------->"+i);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}
//这是个守护线程
class bakdata extends Thread{@Overridepublic void run() {int i=0;//虽然是个死循环,但是用户线程一结束,守护线程自动结束while(true){System.out.println(Thread.currentThread().getName()+"------->"+(++i));try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

运行结果:主线程循环十次输出到10,守护线程察觉到用户线程结束,便也会自动结束。

12.定时器作用和如何实现

作用:

间隔特定的时间,执行特定的程序。
每周要进行银行总账操作。
每天要进行数据备份操作。

在实际开发中,每隔多久执行一个程序是很常见的。在java中有很多种实现方式:

1.可以使用sleep方法,每隔一段时间线程醒来,执行特定的程序,这是最原始的定时器(但是比较low)。
2.在java中已经写好了一个定时器:java.util.Timer,可以直接拿来用,但是这种方式在目前开发中还用得少,因为现在很多高级框架都时支持定时任务的。
3.在实际的开发中,目前是用的比较多的是spring框架中提供的springTask框架,在这个框架中只要进行简单的配置,就可以实现定时器任务。

实现方式:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;public class TimerTest {public static void main(String[] args) {//创建定时器对象Timer timer=new Timer();//获取当前时间Date nowtime=new Date();System.out.println(nowtime);//给日期制定一个格式SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");//SimpleDateFormat里面有一format方法,将现在的时间转换成指定格式//System.out.println(sdf.format(nowtime));timer.schedule(new logtimertask(),nowtime,1000*10);}
}
//public abstract class TimerTask
//extends Object
//implements Runnable
class logtimertask extends TimerTask {@Overridepublic void run() {//编写你需要执行的任务SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");String time=sdf.format(new Date());System.out.println("电脑"+time+" 完成日志记录!");}
}

运行结果:

更多推荐

关于java多线程详细知识点

本文发布于:2024-03-13 11:03:40,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1733846.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:知识点   多线程   详细   java

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!