java多线程基础总结
2021/12/14 22:17:02
本文主要是介绍java多线程基础总结,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Java多线程编程
1,一个线程的生命周期
- 新建状态
- 就绪状态:当线程对象调用了
start()
方法之后,该线程即进入就绪转态;就绪状态的线程处于就绪队列中,要等待JVM线程调度器的调度。 - 运行状态:如果就绪状态的线程获取CPU资源,就可以执行
run()
方法。 - 阻塞状态:若执行了
sleep()
(睡眠),suspend()
(挂起)方法,或者失去了所占用的资源后,线程进入阻塞状态;再次之后,可重新进入就绪状态;- 等待阻塞:运行中执行
wait()
方法。 - 同步阻塞:线程获取synchronized同步锁失败(同步锁被其他线程占用)。
- 其他阻塞:如,通过调用
sleep()
或join()
发出IO请求时,会进入阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
- 等待阻塞:运行中执行
- 死亡状态:线程完成或者终止条件触发;
2,线程优先级
每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )
,1-10级。
默认情况下,每一个线程都会分配一个优先级NORM_PRIORITY(5)
。
3,线程创建的方法
- 继承Thread类;
- 实现Runnable接口;
- Callable和Future创建;
3.0 关于Thread类
3.0.1 Thread类方法
- 1,获取线程的名称:
- 使用Thread类中的方法
String getName()
返回线程的名称 - 可以获取到当前正在执行的线程
static Thread currentThread()
,使用线程中的getName()方法获取线程
- 使用Thread类中的方法
- 2,设置线程名称:
- 使用
void setName(String name)
方法改变线程名称 - 创建一个带参构造方法,参数为线程的名称,调用父类的带参构造方法
Thread(String name)
,让父类给取一个名字;
- 使用
- 3,线程休眠
public static void sleep(long millis)
:使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),毫秒数结束之后,线程继续执行
public class MyThread extends Thread{ @Override public void run(){ System.out.println(getName()); } } public class Demo01GetThread{ //创建Thread类的子类对象 MyThread mt=new MyThread(); //调用start()方法,开启新线程 mt.start();//--------------------------"Thread-0" new MyThread().start();//--------------"Thread-1" new MyThread().start();//--------------"Thread-2" } public class MyThread extends Thread{ @Override public void run(){ Thread t=Thread.currentThread(); System.out.println(t); System.out.println(t.getName()); } } public class Demo01GetThread{ //创建Thread类的子类对象 MyThread mt=new MyThread(); //调用start()方法,开启新线程 mt.start();//--------------------------"Thread[Thread-0,5,main]/Thread-0" new MyThread().start();//--------------"Thread[Thread-1,5,main]/THread-1" new MyThread().start();//--------------"Thread[Thread-2,5,main]/Thread-2" System.out.println(Thread.currentThread().getName());//-------"main" }
public class Demo01GetThread{ //创建Thread类的子类对象 MyThread mt=new MyThread(); mt.setName("张三"); System.out.println(mt.getName()); } public class MyThread extends Thread{ @Override public MyThread(){} public MyThread(String name){ super(name);//调用父类的带参构造方法``Thread(String name),让父类给取一个名字 } public void run(){ Thread t=Thread.currentThread(); System.out.println(t); System.out.println(t.getName()); } }
public class Demo01Sleep{ public static void main(String[] args){ for(int i=1;i<=60;i++){ System.out.println(i); try{ Thread.sleep(1000); }catch(InterruptedException e){ e.printStackTrace(); } } } }
3.1 继承Thread类
将类声明为Tread的子类,该子类重写Thread类的run方法。接下来可以分配该子类的实例。
步骤:
- 1,创建一个Thread类的子类;
- 2,在Thread类的子类中重写Thread类中的
run()
方法,设置线程任务(即线程要做什么); - 3,创建T该子类对象;
- 4,调用Thread类中的
Start()
方法,开启新线程,间接执行run方法;void start()
使该线程开始执行; Java虚拟机调用该线程的run方法。- 结果是两个线程并发地运行:当前线程(main线程)和另一个线程(创建的新线程,执行其run 方法)。
- 多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
java程序属于抢占式调度, 那个线程的优先级高,那个线程优先执行;同一个优先级,随机选择一 个执行
public class MyThread extends Thread{ @Override public void run(){ for(int i=0;i<20;i++){ System.out.println("run:"+i); } } } public class Demo01Tread{ public static void main(String[] args){ //创建子类对象 MyThread mt=new MyThread(); //调用start()方法,间接执行run()方法 mt.start(); //main主线程,此时两线程并发执行 for(int i=0;i<20;i++){ System.out.println("main:"+i) } } }
执行结果:
并发的抢占CPU资源
3.2 实现Runnable接口
创建线程的另-种方法是声明实现Rumable接口的类。该类然后实现run方法。然后可以分配该类的实例,在创建Thread 时作为一个参数来传递并启动。
java. Lang. Runnable
Runnable接口应该由那些打算通过某一线程执行其实例的类来实现。 类必须定义一个称为run 的无参数方法。
java. Lang. Thread类的构造方法
Thread(Runnable target)
分配新的Thread 对象。
Thread(Runnable target, String name)
分配新的Thread 对象。
步骤:
- 1,创建实现Runnable接口的实现类
- 2,重写接口中的
run()
方法 - 3,创建该Runnable接口实现类的对象
- 4,用该对象创建Thread类对象
- 5,调用Thread类对象中的
start()
方法,开启新线程
public class MyRunClass implements Runnable{//--------实现Runnable接口 @Override void run(){ //------------------------------------重写run方法 System.out.println("Runnable线程:"+Thread.currentThread().getName()); } } public class Demo01Runnable{ public static void main(String[] args){ //写法1 MyRunClass rc=new MyRunClass();//--------创建该Runnable接口实现类的对象 Thread mt=new Thread(rc);//----------用该对象创建Thread类对象 mt.start();//----------------------------调用Thread类对象中的start()方法,开启新线程 //写法2 new Thread(){ @Override public void run(){ for(int i=0;i<20;i++){ System.out.println(i); } } }.start(); //写法3 Runnable r=new Runnable(){ @Override public void run(){ for(int i=0;i<20;i++){ System.out.println(i); } } } new Thread(r).start(); //写法4 new Thread(new Runnable(){ @Override public void run(){ for(int i=0;i<20;i++){ System.out.println(i); } } }).start(); //写法5:lambda表达式 new Thread(()->{ for(int i=0;i<20;i++){ System.out.println(i); } }).start(); } }
实现Runnable接口创建新线程的好处:
-
1,避免了单继承的局限性
- 一个类只能继承一一个类(- -个人只能有一-个亲爹),类继承了Thread类就不能继承其他的类
实现了Runnable接口,还可以继承其他的类,实现其他的接口。
- 一个类只能继承一一个类(- -个人只能有一-个亲爹),类继承了Thread类就不能继承其他的类
-
2,增强了程序的扩展性,降低了程序的耦合性
- 实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)。
-
实现Runnable接口的方式,一个任务可以用多个线程来执行。
3.3 实现Callable接口
- 1,实现Callable的接口,需要返回值类型
- 2,重写call方法,需要抛出异常值
- 3,创建目标Callalbe对象,n个;
- 4,创建执行服务对象
ExecutorService ser
:ExecutorService ser =Executors.newFixedThreadPool(n);
- 5,提交执行:
Future<T> res=ser.submit(call1);
- 6,获取结果:
res.get()
- 7,关闭服务:
ser.shutdownNow()
//1,实现Callable的接口,需要返回值类型 public class TestCallable implements Callable<Boolean>{ private String url; private String name; public TestCallable(String url,String name){ this.url=url; this.name=name; } //2, 重写call方法,需要抛出异常值 @Override public Boolean call(){ WebDownloader webDownloader=new WebDownloader(); webDownloader.downloader(url,name); System.out.println("下载了文件名为:"+name); return true; } public static void main(String[] args){ //3,创建目标Callalbe对象,n个; TestCallable t1=new TestCallable("https://kgoe.ego.cn/gei.img/1"); TestCallable t2=new TestCallable("https://kgoe.ego.cn/gei.img/2"); TestCallable t3=new TestCallable("https://kgoe.ego.cn/gei.img/3"); //4,创建执行服务对象ExecutorService ExecutorService ser =Executors.newFixedThreadPool(3); //5,提交执行 Future<Boolean> r1=ser.submit(t1); Future<Boolean> r2=ser.submit(t2); Future<Boolean> r3=ser.submit(t3); //6,获取返回值 boolean rs1=r1.get(); boolean rs2=r2.get(); boolean rs3=r3.get(); //7,关闭执行服务 ser.shutdownNow(); } }
4,多线程内存图解
新的线程会开辟新的栈空间
这篇关于java多线程基础总结的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-10-05小米13T Pro系统合集:性能与摄影的极致融合,值得你升级的系统ROM
- 2024-10-01基于Python+Vue开发的医院门诊预约挂号系统
- 2024-10-01基于Python+Vue开发的旅游景区管理系统
- 2024-10-01RestfulAPI入门指南:打造简单易懂的API接口
- 2024-10-01初学者指南:了解和使用Server Action
- 2024-10-01Server Component入门指南:搭建与配置详解
- 2024-10-01React 中使用 useRequest 实现数据请求
- 2024-10-01使用 golang 将ETH账户的资产平均分散到其他账户
- 2024-10-01JWT用户校验课程:从入门到实践
- 2024-10-01Server Component课程入门指南