Java【9】线程

1、线程概述

1.1 程序、进程、线程

在这里插入图片描述
在这里插入图片描述
进程包含了资源分配!
在这里插入图片描述
线程就是一个执行机构,进程的执行是交给线程去完成的。
在这里插入图片描述
进程是资源分配的独立单位,线程是调度执行的独立单位。
在这里插入图片描述
当一个进程分解出多个线程时,执行效率大大提高,因此有了多线程。

★ 下面四个重要方法,获取当前执行的线程的@¥*/@:
在这里插入图片描述
例如:
在这里插入图片描述
在这里插入图片描述
该程序在执行时会产生一个进程,并对应地产生了一个线程,这个线程来完成本次执行。

上面的程序产生了一个主线程,用来完成主函数中的方法体的内容:
在这里插入图片描述

1.2 多线程

在这里插入图片描述
在这里插入图片描述
上述代码全部是由main线程来执行的!
顺序是先完成所有聊天,再开始完成所有上传操作。并没有达到边聊天边上传的那种真实的“QQ”效果。
在这里插入图片描述
若想达到多线程的效果,如图所示——
在这里插入图片描述
编写一个聊天线程LTThread,继承自Thread类。里面实现/重写了run()方法,该方法的方法体内写好你的这个线程要执行什么东西!
在这里插入图片描述
下面的start()方法用来启动线程,即把线程创建出来以后分配资源
在这里插入图片描述
上图代码的效果是:主线程在执行上传时,穿插着执行了聊天线程的聊天。
(它们在交替地使用CPU)
在这里插入图片描述
在这里插入图片描述
★ 下面再创建出一个上传线程,达到 三个线程 的效果,如图所示:
在这里插入图片描述
// 注:这三个线程都是为了很好地完成进程任务。进程有三个业务,一是qq主业务得执行,二三是聊天和下载也要执行。

把原本的“上传”方法—改写为—>“上传”线程:
在这里插入图片描述
在这里插入图片描述
主函数变更为——
在这里插入图片描述
三个线程的效果如图:
在这里插入图片描述
在这里插入图片描述

2、线程的创建

2.1 继承Thread类创建线程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
补充:下面的办法给线程命名更加便捷——
在这里插入图片描述
在这里插入图片描述
★ 获取当前线程的一些属性——
在这里插入图片描述

2.2 实现Runnable接口创建线程

在这里插入图片描述
★ 使用方式:
在这里插入图片描述
在这里插入图片描述
看下面代码:对于同一个实例化对象 t2,同时开两个线程去执行——
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.3 使用Callable和Future创建线程

注意:这两种也都是接口!!!
在这里插入图片描述
★ 用法:
该方法可以有返回值!!
在这里插入图片描述
在这里插入图片描述
上述代码已经可以执行线程,但是如何获取sum的值呢?
在这里插入图片描述
“获取返回值”可能出现等待时间过长,因此要求做异常处理,下面这个图用了抛出异常——
在这里插入图片描述

2.4 一些特殊创建写法

★ 2.1-2.3 小结
在这里插入图片描述
匿名内部类实现、lambda表达式线程创建···

// 先不学了 有用再说。。

3、线程生命周期

3.1 生命周期描述

在这里插入图片描述

3.2 Thread常用方法

① start()
在这里插入图片描述
② yield() 让步
在这里插入图片描述
在这里插入图片描述
上述两个方法的实际应用——
在这里插入图片描述
在这里插入图片描述
t1让步,所以t2的都执行完了以后,才去搞t1,运行截图如下。
在这里插入图片描述
③ sleep()
在这里插入图片描述
在这里插入图片描述
使用示例:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
④ join()
在这里插入图片描述
在这里插入图片描述

4、线程同步

4.1 线程同步-可见性(非线程安全)

在这里插入图片描述
在这里插入图片描述
★ volatile 关键字 ——
在这里插入图片描述
即,当volatile的变量被修改时,会立刻通知使用他的其他线程,重新读取该变量。
在这里插入图片描述

4.2 线程同步-原子性

原子性:语句并不是一次性就能执行完的,要分解为很多指令部分!这些个指令部分在执行时,中间是可以被打断的。

原子性问题——T1执行完第一个读取指令后,T2抢先把自己三条语句执行了,T1中断现场保护现场又恢复现场以后,继续执行2、3条指令,结果算出来的结果是错的。。。
在这里插入图片描述
上图中:T1在访问变量a时,T2不能去访问a了!!即,变量a不能被两个线程同时访问,否则就会出错!

解决办法:设立临界区,该区同一时间内只允许一个线程进入。
在这里插入图片描述
★ 临界区需要开锁关锁—— synchronized关键词
在这里插入图片描述
// synchronized关键词在下一小节细讲

4.3 synchronized实例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
上图代码中,测试1在使用f1时,测试2也进来了,同时访问了f1,这样就会出错了!!现在我们要把f1()方法变成临界资源——上锁:
在这里插入图片描述
(下图的执行结果显示:起到了“临界资源”的效果!)
在这里插入图片描述
上述代码中,执行t1时要去找实例化对象t要锁,t给了t1线程解锁以后,同一时间内就不会再给另一线程t2解锁。t1执行完以后,t2线程才会获得锁。

★ 注意理解下图概念:
在这里插入图片描述
静态方法的锁——
在这里插入图片描述
上述代码(static静态方法)中,每次执行时已经不再是找类的实例化对象要锁了,而是去找这个类要锁!!!
在这里插入图片描述
第三种,对语句块上锁
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.4 lock接口 & ReentrantLock类

Lock相较于synchronized,需要程序猿人为地去释放锁(在finally语句中)。
// 注意,lock能完成synchronized的所有功能!!
在这里插入图片描述
用法——
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我进门,把门一关,别人就进不来了。当我(某线程)执行完之后,我再把门打开。
在这里插入图片描述

4.5 wait()、notify()、notifyAll()

这三个方法都由Object类提供,用于进程同步。wait()是阻塞方法。notify()唤醒单个线程,notifyAll()唤醒所有线程。

用法——
略。
在这里插入图片描述

5、编程例题

第一,掌握使用Thread子类和Runnable接口创建多线程的方法。
第二,掌握线程的执行过程。

5.1 模拟龟兔赛跑

利用多线程技术编写一个模拟龟兔赛跑的程序,要求如下:
(1)乌龟每次跑一个单位,兔子每次跑10个单位;
(2)每个线程运行时,判断是否达到终点,如果到达终点,给出提示信息,未到终点则提示目前距离终点的距离,并判断是否领先;
(3)如果兔子领先,则显示“我跑得快,睡一觉”信息,并睡一会。

package guitu_saipao;

class Race implements Runnable {
	private static String winner;
	private volatile int rabbitdistance, turtledistance;
	
	public void run() {
		for (int i = 0; i <= 100; i++) {
			if (Thread.currentThread().getName().equals("兔子")) {
				if (gameover(i * 10)) {
					break;
					}
				rabbitdistance = i * 10;
				System.out.println(Thread.currentThread().getName() + "距离终点" + (100 - i * 10));
				if (rabbitdistance > turtledistance) {
					System.out.println("我跑得快,睡一觉");
					try {
						Thread.sleep(0, 1);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			if (Thread.currentThread().getName().equals("乌龟")) {
				if (gameover(i)) {
					break;
				}	
				turtledistance = i;
				System.out.println(Thread.currentThread().getName() + "距离终点" + (100 - i));
			}
		}
	}
	
	private boolean gameover(int steps) {
		if (winner != null) {
			return true;
		}
		if (steps >= 100) {
			winner = Thread.currentThread().getName();
			System.out.println(winner + "获胜");
			return true;
		}
		return false;
	}
}


public class Main {
	public static void main(String[] args) {
		Race race = new Race();
		new Thread(race, "兔子").start();
		new Thread(race, "乌龟").start();
	}
}

5.2 模拟多人过独木桥

编写多线程应用程序,模拟多人过独木桥的模拟。独木桥每次只能通过一个人,每个人通过木桥的时间为5秒,随机生成10个人,同时准备过此独木桥,显示一下每次通过独木桥人的姓名。需要用到随机数。

注意:

(1)开始过桥时输出:开始过桥!过完桥后输出:已过桥!

(2)随机选人的时候,每个人都要选到,不能重复选。

package 多人过独木桥;

import java.util.LinkedHashSet;
import java.util.Random;
import java.util.Set;
public class dumuqiao implements Runnable{
    private static int deng=0;
    public void run() {
                deng= deng+5000;
                try
                {
                    Thread.sleep(deng);              
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" 独木桥");
    }
    
    public static void main(String[] args) {
        String ary[] ={"赵","钱","孙","李","周","吴","郑","王","冯","陈"};
        dumuqiao gsd = new dumuqiao(); 
        Set<Integer> set=new LinkedHashSet<Integer>();
        while(true){
        if(set.size() == 10){
        break;
        }
        //乱序排列(随机)
        int a=(int) (Math.random()*10);
        set.add(a);
        }
        for(int b:set){
        Thread th = new Thread(gsd, ary[b]);
        th.start();
        }            
    }             
}

5.3 火车站卖票

哈尔滨火车站下面有三个火车票代售点:哈站、哈东站、哈西站,假如哈尔滨到北京的火车票总共是200张,如何用程序来实现三个售票点同时卖票的功能。注意:考虑线程同步问题,避免出现重复卖票问题。需要考虑同步问题。

package 火车站售票;

class Ticket implements Runnable{
	 private int TicketNum = 10; //100张火车票
	 private boolean flag = true;
	 
	 private synchronized void sale()
	 {
		 if(TicketNum<=0)
		 {
			 flag = false;
			 return ;
		 }
		 TicketNum--;
		 System.out.println(Thread.currentThread().getName()+"卖了一张票,还剩"+TicketNum+"张票。");
	 }
	 
	 public void run() {
		 while(flag)
		 {
			 sale();	
			 try {
				 Thread.sleep(200);
			 } catch (InterruptedException e) {
				 e.printStackTrace();
			 }
		 }
	 }
}
	
public class Main { 
	public static void main(String[] args) {       
		Ticket t = new Ticket();      
		Thread th1 = new Thread(t,"哈尔滨站");     
		Thread th2 = new Thread(t,"哈尔滨西站");     
		Thread th3 = new Thread(t,"香坊站");   
		th1.start();
		th2.start();    
		th3.start();
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值