Java多线程使用
线程与进程
进程:动态的,程序(任务)执行的过程 ,持有资源(共享内存,共享文件)和线程。
线程:系统中最小的执行单元。同一个进程中有多个线程,线程之间共享进程资源。
线程与进程的比较
1. 调度。
在传统的操作系统中,拥有资源和独立调度的基本单位都是进程。在引入线程的操作系统中,线程是独立调度的基本单位,进程是资源拥有的基本单位。在同一进程中,线程的切换不会引起进程切换。在不同进程中进行线程切换,如从一个进程内的线程切换到另一个进程中的线程时,会引起进程切换。
2. 拥有资源。
不论是传统操作系统还是设有线程的操作系统,进程都是拥有资源的基本单位,而线程不拥有系统资源(也有一点必不可少的资源),但线程可以访问其隶属进程的系统资源。
3. 并发性。
在引入线程的操作系统中,不仅进程之间可以并发执行,而且多个线程之间也可以并发执行,从而使操作系统具有更好的并发性,提高了系统的吞吐量。
4. 系统开销。
由于创建或撤销进程时,系统都要为之分配或回收资源,如内存空间、
I/O设备等,因此操作系统所付出的开销远大于创建或撤销线程时的开销。类似地,在进行进程切换时,涉及当前执行进程CPU环境的保存及新调度到进程CPU环境的设置,而线程切换时只需保存和设置少量寄存器内容,开销很小。此外,由于同一进程内的多个线程共享进程的地址空间,因此,这些线程之间的同步与通信非常容易实现,甚至无需操作系统的干预。
5. 地址空间和其他资源(如打开的文件):进程的地址空间之间互相独立,同一进程的各线程间共享进程的资源,某进程内的线程对于其他进程不可见。
6. 通信方面:进程间通信(IPC)需要进程同步和互斥手段的辅助,以保证数据的一致性,而线程间可以直接读/写进程数据段(如全局变量)来进行通信。
线程的实现方式
线程的实现可以分为两类:用户级线程(User-LevelThread, ULT)和内核级线程(Kemel-LevelThread, KLT)。内核级线程又称为内核支持的线程。
在用户级线程中,有关线程管理的所有工作都由应用程序完成,内核意识不到线程的存在。应用程序可以通过使用线程库设计成多线程程序。通常,应用程序从单线程起始,在该线程中开始运行,在其运行的任何时刻,可以通过调用线程库中的派生例程创建一个在相同进程中运行的新线程。图2-2(a)说明了用户级线程的实现方式。
在内核级线程中,线程管理的所有工作由内核完成,应用程序没有进行线程管理的代码,只有一个到内核级线程的编程接口。内核为进程及其内部的每个线程维护上下文信息,调度也是在内核基于线程架构的基础上完成。图2-2(b)说明了内核级线程的实现方式。
线程交互方式
1. 互斥
2. 同步
线程的状态及生命周期
线程有就绪、挂起,阻塞和运行等基本状态。
java线程的实现
java里线程的实现有两种方法,如下:
1. 继承Thread类
Thread类定义在java.lang包中,继承Thread类必须重写run方法。
定义格式:
class className extend Thread { run() {} }
Thread常用方法
类别 | 方法签名 | 介绍 |
线程的创建 | Thread() | |
Thread(String name) | ||
Thread(Runnable target) | ||
Thread(Runnable target, String name) | ||
线程的方法 | void start() | 启动线程 |
static void sleep(long millis) | 线程休眠 | |
static void sleep(long millis, int nanos) | ||
void join() | 让其他线程等待当前线程终止 | |
void join(long millis) | ||
void join(ong millis, int nanos) | ||
static void yield() |
线程让步:当前运行中的线程释放处理器资源,重新竞争。 就是让当前线程由“运行状态”进入到“就绪状态”, 从而让其它具有相同优先级的等待线程获取执行权。 |
|
获取线程引用 | static Thread currentThread() | 返回当前运行的线程引用 |
MyThread.java
package xiancheng; public class MyThread extends Thread{ private String name; //线程的名字 //构造方法 public MyThread(String name) { this.name = name; } public void run() { for (int i = 0; i < 10; i++) { //因为cpu执行很快,这里循环次数可以多点,可以看到效果 System.out.println(name +":"+ i); } super.run(); } }
Threaddemo.java
package xiancheng; public class ThreadDemo1 { public static void main(String[] args) { MyThread t1 = new MyThread("线程A"); MyThread t2 = new MyThread("线程B"); //同时启动线程执行,线程的启动是通过start() t1.start(); t2.start(); } }
执行Threaddemo.java文件
上面截图结果都是并发执行生成的,只要抢到cpu资源就可以执行并输出。
2. 实现Runnable接口
创建MyRunnable.java文件,代码如下:
package xiancheng; public class MyRunnable implements Runnable{ private String name; //线程的名字 public MyRunnable(String name) { this.name = name; } @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(name +":"+ i); } } }
执行Threaddemo.java文件
java线程的状态
1. 创建状态: 准备一个多线程的对象,比如一个new Thread()对象。
2. 就绪状态:调用start()方法,等待CPU进行调度。当一个调用start()方法时候还没表面改线程可以马上运行了,还必须等待CPU调度才可以。
3. 运行状态:执行run()方法。
4. 阻塞状态:暂停执行,可能将资源交给其他线程使用。
5. 终止状态:线程销毁,也是死亡状态。
java线程常用方法
线程大部分方法在Thread类中,下面看看线程一些常用方法。
1. getName():取得线程名称。
2. currentThread():获取当前线程对象。
3. isAlive():线程是否启动。
4. join():线程强制运行。
5. sleep():线程休眠。
6. yiled():暂停当前正在执行的线程对象,并执行其他线程。
下面是运行demo
package xiancheng; class MyRunnabled implements Runnable{ private String name; //线程的名字 public MyRunnabled(String name) { this.name = name; } public void run() { System.out.println("当前线程对象:"+Thread.currentThread()); System.out.println("当前线程对象name:"+Thread.currentThread().getName()); } } public class ThreadDemo1 { public static void main(String[] args) { MyRunnabled r = new MyRunnabled("线程A"); //创建Thread类 通过Thread类去启动线程 Thread t = new Thread(r); System.out.println("当前线程是否启动:"+t.isAlive()); t.start(); System.out.println("当前线程是否启动:"+t.isAlive()); } }
运行结果
使用join()方法
package xiancheng; class MyRunnabled implements Runnable{ private String name; //线程的名字 public MyRunnabled(String name) { this.name = name; } public void run() { for (int i = 0; i < 50; i++) { System.out.println(name +":"+ i); } } } public class ThreadDemo1 { public static void main(String[] args) { MyRunnabled r = new MyRunnabled("线程A"); //创建Thread类 通过Thread类去启动线程 Thread t = new Thread(r); t.start(); for (int i = 0; i < 50; i++) { if (i > 10) { try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("主线程:"+i); } } }
运行结果
将主线程暂停,使用join强制运行自己的线程。
使用sleep()方法
package xiancheng; class MyRunnabled implements Runnable{ private String name; public MyRunnabled(String name) { this.name = name; } public void run() { for (int i = 0; i < 50; i++) { try { Thread.sleep(1000);//沉睡一秒 下面就每隔一秒输出一次直到该循环结束 System.out.println(name +":"+ i); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class ThreadDemo1 { public static void main(String[] args) { MyRunnabled r = new MyRunnabled("线程A"); //创建Thread类 通过Thread类去启动线程 Thread t = new Thread(r); t.start(); } }
使用yiled()方法:在线程执行时候,在循环体中判断如果i=10就执行yiled()方法,将当前执行权限交给别的线程。
class MyRunnabled implements Runnable{ private String name; public MyRunnabled(String name) { this.name = name; } public void run() { for (int i = 0; i < 50; i++) { System.out.println(name +":"+ i); if (i == 10) { System.out.println("开始调用yiled()方法"); Thread.yield(); } } } } public class ThreadDemo1 { public static void main(String[] args) { MyRunnabled r1 = new MyRunnabled("线程A"); MyRunnabled r2 = new MyRunnabled("线程B"); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start(); } }
java线程优先级
java中的线程优先级的范围是1~10,默认的优先级是5。10极最高。java线程优先级设置有如下参数:
1 - MIN_PRIORITY
5 - NORM_PRIORITY
10 - MAX_PRIORITY
注意:线程优先级有可能影响线程的执行顺序,不是一定能影响。
package xiancheng; class MyRunnabled implements Runnable{ public void run() { for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); System.out.println(Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class ThreadDemo1 { public static void main(String[] args) { Thread t1 = new Thread(new MyRunnabled(), "线程A"); // 通过匿名对象方式 Thread t2 = new Thread(new MyRunnabled(), "线程B"); Thread t3 = new Thread(new MyRunnabled(), "线程C"); //设置线程优先级 t1.setPriority(Thread.MIN_PRIORITY); t2.setPriority(Thread.NORM_PRIORITY); t3.setPriority(Thread.MAX_PRIORITY); //启动线程 t1.start(); t2.start(); t3.start(); } }
可以看到线程C每次大概率会先执行。
java线程同步与死锁
1. 同步代码块
在java代码块上加上 synchronized 关键字,则该代码块就是同步代码块。
2. 同步代码块格式
synchronized (同步对象) {
需要同步的代码块;
}
代码块可以同步,方法也可以同步。方法同步格式如下:
synchronized void 方法名称 (){}
具体使用,先看下面一个案例
class MyRunnabled implements Runnable{ private int ticket = 5; public void run() { for (int i = 0; i < 10; i++) {
if (ticket>0) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("车票"+ ticket--);
} } } } public class ThreadDemo1 { public static void main(String[] args) { MyRunnabled m = new MyRunnabled(); Thread t1 = new Thread(m); Thread t2 = new Thread(m); Thread t3 = new Thread(m); //启动线程 t1.start(); t2.start(); t3.start(); } }
运行结果如下,出现负数,当三个线程执行的时候,由于对ticket数值互相不清楚,也就是资源无法共享导致的。
这里就可以使用synchronized关键字,来保证临界资源(ticket)的安全性。
package xiancheng; class MyRunnabled implements Runnable{ private int ticket = 5; public void run() { for (int i = 0; i < 10; i++) { synchronized (this) { if (ticket > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("车票"+ ticket--); } } } } } public class ThreadDemo1 { public static void main(String[] args) { MyRunnabled m = new MyRunnabled(); Thread t1 = new Thread(m); Thread t2 = new Thread(m); Thread t3 = new Thread(m); //启动线程 t1.start(); t2.start(); t3.start(); } }
代码运行会变慢一点,但是此时ticket数据不会出现负数了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步