大家好,欢迎来到IT知识分享网。
文章目录
第四章 多线程
进程:应用程序的执行实例,有独立的内存空间和系统资源
线程:cpu调度和分派的基本单位,进程中执行运算的最小单位,可完成一个独立的顺序控制流程
1.多线程
什么是多线程:
如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程”
多个线程交替占用CPU资源,而非真正的并行执行
多线程好处:
充分利用CPU的资源、简化编程模型、带来良好的用户体验
2.主线程
Thread类:Java提供了java.lang.Thread类支持多线程编程
主线程:
- main()方法即为主线程入口
- 产生其他子线程的线程
- 必须最后完成执行,因为它执行各种关闭动作
public static void main(String args[]) {
Thread t= Thread.currentThread(); System.out.println("当前线程是: "+t.getName()); t.setName("MyJavaThread"); System.out.println("当前线程名是: "+t.getName()); }
3.线程的创建和启动
创建线程的三种方式:
- 继承java.lang.Thread类
- 定义MyThread类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
//继承Thread类 public class MyThread extends Thread{ //重写run()方法,编写线程执行体 @Override public void run() { for (int i = 1;i < 100; i++){ System.out.println(Thread.currentThread().getName()+":"+i); } super.run(); } public static void main(String[] args) { //创建线程对象 MyThread thread = new MyThread(); //调用start()方法启动线程 thread.start(); } }
- 多个线程交替执行,不是真正的“并行”
- 线程每次执行时长由分配的CPU时间片长度决定
MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); t1.start(); t2.start(); //运行结果: Thread-0:1 Thread-0:2 Thread-1:1 Thread-1:2 Thread-1:3 Thread-1:4 Thread-1:5 Thread-0:3 Thread-0:4
直接调用run()方法和调用start()方法:
调用run()方法:
主线程执行run(),只有主线程一条执行路径
调用start()方法:
子线程执行run()方法,多条执行路径,主线程和子线程并行交替执行
- 实现java.lang.Runnable接口
- 定义MyRunnable类实现Runnable接口
- 实现run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
//实现Runnable接口 public class MyRunnable implements Runnable{ //实现run()方法 @Override public void run() { for(int i=1;i<100;i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } public static void main(String[] args) { //创建线程对象 MyRunnable myRunnable = new MyRunnable(); Thread myThread = new Thread(myRunnable); //启动线程 myThread.start(); } }
比较两种创建线程的方式:
- 继承Thread类
- 编写简单,可直接操作线程
- 适用于单继承
- 实现Runnable接口
- 避免单继承局限性
- 便于共享资源
- 实现Callable<数据类型>接口
- 定义MyCallable类实现Callable<数据类型>接口
- 重写call方法(Callable有返回值),编写线程执行体
import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class MyCallable implements Callable<String> { @Override public String call() throws Exception { for(int i=1;i<=10;i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } return "MyCallable线程"; } public static void main(String[] args) { MyCallable myCallable = new MyCallable(); FutureTask<String> futureTask1 = new FutureTask<>(myCallable); FutureTask<String> futureTask2 = new FutureTask<>(myCallable); Thread thread1 = new Thread(futureTask1); thread1.start(); Thread thread2 = new Thread(futureTask2); thread2.start(); //接收返回值 try { System.out.println(futureTask1.get()); }catch (Exception e){ e.printStackTrace(); } } }
使用线程的步骤:
定义线程、创建线程对象、启动线程、终止线程
4.线程的状态
创建状态、就绪状态、阻塞状态、运行状态、死亡状态
5.线程调度
线程调度指按照特定机制为多个线程分配CPU的使用权
方法 | 说明 |
---|---|
void setPriority(int newPriority) | 更改线程的优先级 |
static void sleep(long millis) | 在指定的毫秒数内让当前正在执行的线程休眠 |
void join() | 等待该线程终止 |
static void yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
void interrupt() | 中断线程 |
boolean isAlive() | 测试线程是否处于活动状态 |
1.线程优先级
线程优先级是一个整数,范围在1(Thread.MIN_PRIORITY)到10(Thread.MAX_PRIORITY)之间。
默认优先级为5(Thread.NORM_PRIORITY),优先级高的线程获得CPU资源的概率较大
//setPriority(int newPriority) Thread t1 = new Thread(new MyThread(),"线程A"); Thread t2 = new Thread(new MyThread(),"线程B"); //线程A最高优先级 t1.setPriority(Thread.MAX_PRIORITY); t1.start(); //线程B最低优先级 t2.setPriority(Thread.MIN_PRIORITY); t2.start();
2.线程休眠
让线程暂时睡眠指定时长,线程进入阻塞状态,睡眠时间过后线程会再进入可运行状态
//static void sleep(long millis) millis为休眠时长,以毫秒为单位 for (int i = 0; i < 10; i++) {
System.out.println(i + 1 + "秒"); try {
//调用sleep()方法需处理InterruptedException异常 Thread.sleep(1000); } catch (InterruptedException e) {
e.printStackTrace(); } }
//多线程: //定制日期格式 SimpleDateFormat forMater = new SimpleDateFormat("yyyy- MM-dd HH:mm:ss"); Thread t1 = new Thread(new MyThread(),"线程A"); System.out.println("线程A is running."); System.out.println("当前时间是:"+forMater.format(new Date())); try {
//调用sleep()方法需处理InterruptedException异常 t1.sleep(2000); } catch (InterruptedException e) {
e.printStackTrace(); } System.out.println("线程A is finished."); System.out.println("当前时间是:"+forMater.format(new Date())); Thread t2 = new Thread(new MyThread(),"线程B"); System.out.println("线程B is running."); System.out.println("当前时间是:"+forMater.format(new Date())); try {
//调用sleep()方法需处理InterruptedException异常 t2.sleep(5000); } catch (InterruptedException e) {
e.printStackTrace(); } System.out.println("线程B is finished."); System.out.println("当前时间是:"+forMater.format(new Date())); }
3.线程的强制运行
强制执行当前线程,join写在哪个线程,就阻塞谁
public final void join() public final void join(long mills)//millis:以毫秒为单位的等待时长 public final void join(long mills,int nanos)//nanos:要等待的附加纳秒时长 需处理InterruptedException异常
Thread temp = new Thread(new MyThread()); temp.start(); for(int i=0;i<20;i++){
if(i==5){
try {
temp.join(); } catch (InterruptedException e) {
e.printStackTrace(); }} System.out.println(Thread.currentThread().getName()+"运行:"+i); }
4.线程的礼让
暂停当前线程,允许其他具有相同优先级的线程获得运行机会
该线程处于就绪状态,不转为阻塞状态
只是提供一种可能,但是不能保证一定会实现礼让
public void run() {
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread(). getName()+"正在运行:"+i); if(i==2){
System.out.print("线程礼让:"); Thread.yield(); } } } public static void main(String[] args) {
Yield yield = new Yield(); Thread t1 = new Thread(yield,"线程A"); t1.setPriority(3); Thread t2 = new Thread(yield,"线程B"); t2.setPriority(3); t2.start(); t1.start(); } //运行结果: 线程B正在运行:0 线程A正在运行:0 线程B正在运行:1 线程B正在运行:2 线程礼让:线程A正在运行:1 线程A正在运行:2 线程礼让:线程B正在运行:3 线程B正在运行:4 线程A正在运行:3 线程A正在运行:4
5.线程中断
调用interrupt()方法,立刻改变的是中断状态,但如果不是在阻塞态,就不会抛出异常;
如果在进入阻塞态后,中断状态为已中断,就会立刻抛出异常
public class test1 {
public static void main(String[] args) {
Thread thread = new Thread(){
public void run() {
System.out.println("线程启动了"); while (!isInterrupted()) {
System.out.println(isInterrupted());//调用 interrupt 之后为true } System.out.println("线程结束了"); } } thread.start(); try {
Thread.sleep(1000); } catch (InterruptedException e) {
e.printStackTrace(); } thread.interrupt();//作用是:在线程阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态 } }
1.sleep() & interrupt()
线程A正在使用sleep()暂停着: Thread.sleep(),如果要取消它的等待状态,可以在正在执行的线程里(比如这里是B)调用a.interrupt()[a是线程A对应到的Thread实例],令线程A放弃睡眠操作。即,在线程B中执行a.interrupt(),处于阻塞中的线程a将放弃睡眠操作。
当在sleep中时线程被调用interrupt()时,就马上会放弃暂停的状态并抛出InterruptedException。抛出异常的,是A线程。
2.wait() & interrupt()
线程A调用了wait()进入了等待状态,也可以用interrupt()取消。不过这时候要注意锁定的问题。线程在进入等待区,会把锁定解除,当对等待中的线程调用interrupt()时,会先重新获取锁定,再抛出异常。在获取锁定之前,是无法抛出异常的。
3.join() & interrupt()
当线程以join()等待其他线程结束时,当它被调用interrupt(),它与sleep()时一样,会马上跳到catch块里.。
6.多线程共享数据引发的问题
多个线程操作同一共享资源时,将引发数据不安全问题
同步方法: 解决并发问题
使用synchronized
修饰的方法控制对类成员变量的访问
访问修饰符 synchronized 返回类型 方法名(参数列表){……}
synchronized 访问修饰符 返回类型 方法名(参数列表){……}
public synchronized void sale() {
if (count <= 0) {
flag = true; return; }
同步代码块:
使用synchronized
关键字修饰的代码块
synchronized(syncObject){
//需要同步的代码 } syncObject为需同步的对象,通常为this 效果与同步方法相同
多个并发线程访问同一资源的同步代码块时:
- 同一时刻只能有一个线程进入synchronized(this)同步代码块
- 当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定
- 当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非synchronized(this)同步代码
7.线程安全的类型
方法是否同步 | 效率比较 | 适合场景 | |
---|---|---|---|
线程安全 | 是 | 低 | 多线程并发共享资源 |
非线程安全 | 否 | 高 | 单线程 |
查看ArrayList类的add()方法定义:
public boolean add(E e) {
ensureCapacityInternal(size + 1); //集合扩容,确保能新增数据 elementData[size++] = e; //在新增位置存放数据 return true; }
ArrayList类的add()方法为非同步方法
当多个线程向同一个ArrayList对象添加数据时,可能出现数据不一致问题
ArrayList为非线程安全的类型
常见类型对比:
Hashtable && HashMap:
Hashtable:
继承关系:实现了Map接口,Hashtable继承Dictionary类
线程安全,效率较低
键和值都不允许为null
HashMap:
继承关系:实现了Map接口,继承AbstractMap类
非线程安全,效率较高
键和值都允许为null
StringBuffer && StringBuilder: 前者线程安全,后者非线程安全
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/126886.html