大家好,感谢邀请,今天来为大家分享一下深入解析多线程编程技术与应用的问题,以及和的一些困惑,大家要是还不太明白的话,也没有关系,因为接下来将为大家分享,希望可以帮助到大家,解决大家的问题,下面就开始吧!
每个进程都有自己的内存空间和系统资源
线程:是进程中的单个顺序控制流,一条执行路径
单线程:如果一个进程只有一条执行路径,则称为单线程程序。
多线程:如果一个进程有多个执行路径,则称为多线程程序。
1.2实现多线程方式一:继承Thread类【应用】
方法介绍
方法名称说明void run() 线程启动后,会调用该方法执行void start() 来启动线程。 Java虚拟机将调用run方法()来执行这些步骤。
定义一个类MyThread,继承Thread类,并重写MyThread类中的run()方法。创建MyThread类的对象并开始线程代码演示。
公共类MyThread 扩展Thread {
@覆盖
公共无效运行(){
for(int i=0; i100; i++) {
System.out.println(i);
}
}
}
公共类MyThreadDemo {
公共静态无效主(字符串[] args){
MyThread my1=new MyThread();
MyThread my2=new MyThread();
//my1.run();
//my2.run();
//void start() 导致该线程开始执行; Java虚拟机调用该线程的run方法
my1.start();
my2.start();
}
}两个小问题
为什么我们应该重写run() 方法?
因为run()是用来封装线程执行的代码的
run() 方法和start() 方法有什么区别?
run():封装了线程执行的代码,直接调用,相当于调用普通方法。
start():启动线程;然后该线程的run()方法被JVM调用
1.3设置和获取线程名称【应用】
方法介绍
方法名称说明void setName(String name) 更改此线程的名称,使其等于参数nameString getName() 返回此线程的名称Thread currentThread() 返回当前正在执行的线程对象的引用代码演示
公共类MyThread 扩展Thread {
公共MyThread() {}
公共MyThread(字符串名称){
超级(名称);
}
@覆盖
公共无效运行(){
for (int i=0; i 100; i++) {
System.out.println(getName()+":"+i);
}
}
}
公共类MyThreadDemo {
公共静态无效主(字符串[] args){
MyThread my1=new MyThread();
MyThread my2=new MyThread();
//void setName(String name): 改变本线程的名称等于参数名称
my1.setName("高铁");
my2.setName("飞机");
//线程(字符串名称)
MyThread my1=new MyThread("高铁");
MyThread my2=new MyThread("飞机");
my1.start();
my2.start();
//static Thread currentThread() 返回当前正在执行的线程对象的引用
System.out.println(Thread.currentThread().getName());
}
}
1.4线程优先级【应用】
线程调度
两种调度方式
分时调度模型:所有线程轮流使用CPU,每个线程占用CPU的时间片均匀分布。抢占式调度模型:优先让优先级高的线程使用CPU。如果线程具有相同的优先级,则将随机选择它们。首先,优先级高的线程获得相对较多的CPU时间片。 Java 使用抢占式调度模型。
随机性
如果计算机只有一个CPU,那么该CPU在某一时刻只能执行一条指令,而线程只有在获得CPU时间片,即使用权后才能执行该指令。因此,多线程程序的执行是随机的,因为谁抢到了CPU的使用权并不确定。
优先级相关方法
方法名称说明Final int getPriority() 返回本线程的优先级Final void setPriority(int newPriority) 改变本线程的优先级线程的默认优先级为5;线程优先级范围为:1-10 代码演示
公共类ThreadPriority 扩展Thread {
@覆盖
公共无效运行(){
for (int i=0; i 100; i++) {
System.out.println(getName() + ":" + i);
}
}
}
公共类ThreadPriorityDemo {
公共静态无效主(字符串[] args){
ThreadPriority tp1=new ThreadPriority();
ThreadPriority tp2=new ThreadPriority();
ThreadPriority tp3=new ThreadPriority();
tp1.setName("高铁");
tp2.setName("飞机");
tp3.setName("汽车");
//public Final int getPriority():返回该线程的优先级
System.out.println(tp1.getPriority()); //5
System.out.println(tp2.getPriority()); //5
System.out.println(tp3.getPriority()); //5
//public final void setPriority(int newPriority): 改变本线程的优先级
//tp1.setPriority(10000); //非法参数异常
System.out.println(Thread.MAX_PRIORITY); //10
System.out.println(Thread.MIN_PRIORITY); //1
System.out.println(Thread.NORM_PRIORITY); //5
//设置正确的优先级
tp1.setPriority(5);
tp2.setPriority(10);
tp3.setPriority(1);
tp1.start();
tp2.start();
tp3.start();
}
}
1.5线程控制【应用】
相关方法
方法名称说明: static void sleep(long millis) 使当前正在执行的线程停留(暂停执行)指定的毫秒数。 void join() 等待该线程的死亡。 void setDaemon(boolean on) 将此线程标记为守护线程。当运行的线程均为守护线程时,Java虚拟机将退出代码演示
睡眠演示:
公共类ThreadSleep 扩展Thread {
@覆盖
公共无效运行(){
for (int i=0; i 100; i++) {
System.out.println(getName() + ":" + i);
尝试{
线程睡眠(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
公共类ThreadSleepDemo {
公共静态无效主(字符串[] args){
ThreadSleep ts1=new ThreadSleep();
ThreadSleep ts2=new ThreadSleep();
ThreadSleep ts3=new ThreadSleep();
ts1.setName("曹操");
ts2.setName("刘备");
ts3.setName("孙权");
ts1.start();
ts2.start();
ts3.start();
}
}
加入演示:
公共类ThreadJoin 扩展Thread {
@覆盖
公共无效运行(){
for (int i=0; i 100; i++) {
System.out.println(getName() + ":" + i);
}
}
}
公共类ThreadJoinDemo {
公共静态无效主(字符串[] args){
ThreadJoin tj1=new ThreadJoin();
ThreadJoin tj2=new ThreadJoin();
ThreadJoin tj3=new ThreadJoin();
tj1.setName("康熙");
tj2.setName("四哥");
tj3.setName("八王子");
tj1.start();
尝试{
tj1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
tj2.start();
tj3.start();
}
}
守护进程演示:
公共类ThreadDaemon 扩展Thread {
@覆盖
公共无效运行(){
for (int i=0; i 100; i++) {
System.out.println(getName() + ":" + i);
}
}
}
公共类ThreadDaemonDemo {
公共静态无效主(字符串[] args){
ThreadDaemon td1=new ThreadDaemon();
ThreadDaemon td2=new ThreadDaemon();
td1.setName("关羽");
td2.setName("张飞");
//设置主线程为刘备
Thread.currentThread().setName("刘备");
//设置守护线程
td1.setDaemon(true);
td2.setDaemon(true);
td1.start();
td2.start();
for(int i=0; i10; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
1.6线程的生命周期【理解】
线程有五种状态,线程在各种状态之间转换。
[图片上传失败.(image-2ba3af-1604492765214)]
1.7实现多线程方式二:实现Runnable接口【应用】
螺纹构造方法
方法名称说明Thread(Runnable target) 分配一个新的Thread 对象Thread(Runnable target, String name) 分配一个新的Thread 对象实现步骤
定义一个类MyRunnable来实现Runnable接口。重写MyRunnable类中的run()方法。创建MyRunnable 类的对象。创建Thread类的对象。使用MyRunnable对象作为构造函数的参数来启动线程代码演示。
公共类MyRunnable 实现Runnable {
@覆盖
公共无效运行(){
for(int i=0; i100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
公共类MyRunnableDemo {
公共静态无效主(字符串[] args){
//创建MyRunnable类的对象
MyRunnable my=new MyRunnable();
//创建Thread类的对象,并使用MyRunnable对象作为构造方法的参数
//线程(可运行目标)
//线程t1=new Thread(my);
//线程t2=new Thread(my);
//线程(可运行目标,字符串名称)
Thread t1=new Thread(my,"高铁");
线程t2=new Thread(my,"飞机");
//启动线程
t1.start();
t2.start();
}
}多线程的实现有两种方式
继承Thread类实现Runnable接口具有继承Thread类并实现Runnable接口的优点。
避免了Java单继承的限制
适合同一个程序的多个代码处理同一个资源。它有效地分离了线程和程序的代码和数据,更好地体现了面向对象的设计思想。
2.线程同步
2.1卖票【应用】
案例要求
某电影院目前正在放映一部国产大片,共有100张门票,有3个窗口售票。请设计一个程序来模拟电影院售票。
实施步骤
定义一个类SellTicket来实现Runnable接口,并在其中定义一个成员变量:private int Tickets=100;
重写SellTicket类中的run()方法来售票。代码步骤如下
如果判断票数大于0,则进行售票,并通知售票窗口。
售票后,总票数将减少1
如果票没了,可能会有人来询问,所以这里用了一个死循环来保持卖票的动作继续运行。
定义一个测试类SellTicketDemo,它有一个main方法。代码步骤如下
创建SellTicket 类的对象
创建三个Thread类对象,使用SellTicket对象作为构造方法的参数,并给出对应的窗口名称。
启动线程
代码实现
公共类SellTicket 实现Runnable {
私人int 门票=100;
//重写SellTicket类中的run()方法来售票。代码步骤如下
@覆盖
公共无效运行(){
而(真){
如果(票0){
System.out.println(Thread.currentThread().getName() + "特价" + 门票+ "门票");
门票——;
}
}
}
}
公共类SellTicketDemo {
公共静态无效主(字符串[] args){
//创建SellTicket类的对象
SellTicket st=new SellTicket();
//创建三个Thread类对象,使用SellTicket对象作为构造方法的参数,并给出对应的窗口名称
线程t1=new Thread(st,"窗口1");
线程t2=new Thread(st,"窗口2");
线程t3=new Thread(st,"窗口3");
//启动线程
t1.start();
t2.start();
t3.start();
}
}执行结果
[图片上传失败.(image-81215a-1604492765214)]
2.2卖票案例的问题【理解】
售票出现问题
同一张票出现多次
有反对票
问题原因
由线程执行的随机性引起
公共类SellTicket 实现Runnable {
私人int 门票=100;
@覆盖
公共无效运行(){
//同一张票出现多次
//而(真){
////门票=100;
////t1,t2,t3
////假设t1线程抢占了CPU的执行权
//如果(票0){
////通过sleep()方法模拟出票时间
//尝试{
//线程.sleep(100);
////T1线程休息100毫秒
////t2线程抢占CPU的执行权,t2线程开始执行。当执行到达此点时,t2 线程休息100 毫秒。
////t3线程抢占CPU的执行权,t3线程开始执行。当执行到达此点时,t3 线程休息100 毫秒。
//} catch (InterruptedException e) {
//e.printStackTrace();
//}
////假设线程按顺序唤醒
////t1抢占CPU的执行权,并在控制台输出:Window 1 is sell the 100th Ticket
//System.out.println(Thread.currentThread().getName() + "特价" + 门票+ "门票");
////t2抢占CPU的执行权,并在控制台输出:Window 2 is sell the 100th Ticket
////t3抢占CPU的执行权,并在控制台输出:Window 3 is sell the 100th Ticket
//门票--;
////如果这三个线程还是按顺序来的,这里会进行3次--操作,最终投票会变成97
//}
//}
//出现负票
而(真){
//门票=1;
//t1,t2,t3
//假设t1线程抢占CPU的执行权
如果(票0){
//通过sleep()方法模拟出票时间
尝试{
线程睡眠(100);
//T1线程休息100毫秒
//t2线程抢占CPU的执行权,t2线程开始执行。当执行到达此点时,t2 线程休息100 毫秒。
//t3线程抢占CPU的执行权,t3线程开始执行。当执行到达此点时,t3 线程休息100 毫秒。
} catch (InterruptedException e) {
e.printStackTrace();
}
//假设线程按顺序唤醒
//t1抢到了C
PU的执行权,在控制台输出:窗口1正在出售第1张票 //假设t1继续拥有CPU的执行权,就会执行tickets--;操作,tickets = 0; //t2抢到了CPU的执行权,在控制台输出:窗口1正在出售第0张票 //假设t2继续拥有CPU的执行权,就会执行tickets--;操作,tickets = -1; //t3抢到了CPU的执行权,在控制台输出:窗口3正在出售第-1张票 //假设t2继续拥有CPU的执行权,就会执行tickets--;操作,tickets = -2; System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票"); tickets--; } } } }2.3同步代码块解决数据安全问题【应用】
安全问题出现的条件 是多线程环境 有共享数据 有多条语句操作共享数据 如何解决多线程安全问题呢? 基本思想:让程序没有安全问题的环境怎么实现呢? 把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可 Java提供了同步代码块的方式来解决 同步代码块格式: synchronized(任意对象) { 多条语句操作共享数据的代码 }synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁 同步的好处和弊端 好处:解决了多线程的数据安全问题 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率 代码演示 public class SellTicket implements Runnable { private int tickets = 100; private Object obj = new Object(); @Override public void run() { while (true) { //tickets = 100; //t1,t2,t3 //假设t1抢到了CPU的执行权 //假设t2抢到了CPU的执行权 synchronized (obj) { //t1进来后,就会把这段代码给锁起来 if (tickets >0) { try { Thread.sleep(100); //t1休息100毫秒 } catch (InterruptedException e) { e.printStackTrace(); } //窗口1正在出售第100张票 System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票"); tickets--; //tickets = 99; } } //t1出来了,这段代码的锁就被释放了 } } } public class SellTicketDemo { public static void main(String[] args) { SellTicket st = new SellTicket(); Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3"); t1.start(); t2.start(); t3.start(); } }2.4同步方法解决数据安全问题【应用】
同步方法的格式 同步方法:就是把synchronized关键字加到方法上 修饰符 synchronized 返回值类型 方法名(方法参数) { 方法体; }同步方法的锁对象是什么呢? this 静态同步方法 同步静态方法:就是把synchronized关键字加到静态方法上 修饰符 static synchronized 返回值类型 方法名(方法参数) { 方法体; }同步静态方法的锁对象是什么呢? 类名.class 代码演示 public class SellTicket implements Runnable { private static int tickets = 100; private int x = 0; @Override public void run() { while (true) { sellTicket(); } } // 同步方法 // private synchronized void sellTicket() { // if (tickets >0) { // try { // Thread.sleep(100); // } catch (InterruptedException e) { // e.printStackTrace(); // } // System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票"); // tickets--; // } // } // 静态同步方法 private static synchronized void sellTicket() { if (tickets >0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票"); tickets--; } } } public class SellTicketDemo { public static void main(String[] args) { SellTicket st = new SellTicket(); Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3"); t1.start(); t2.start(); t3.start(); } }2.5线程安全的类【理解】
StringBuffer 线程安全,可变的字符序列 从版本JDK 5开始,被StringBuilder 替代。 通常应该使用StringBuilder类,因为它支持所有相同的操作,但它更快,因为它不执行同步 Vector 从Java 2平台v1.2开始,该类改进了List接口,使其成为Java Collections Framework的成员。 与新的集合实现不同, Vector被同步。 如果不需要线程安全的实现,建议使用ArrayList代替VectorHashtable 该类实现了一个哈希表,它将键映射到值。 任何非null对象都可以用作键或者值从Java 2平台v1.2开始,该类进行了改进,实现了Map接口,使其成为Java Collections Framework的成员。 与新的集合实现不同, Hashtable被同步。 如果不需要线程安全的实现,建议使用HashMap代替Hashtable2.6Lock锁【应用】
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化 ReentrantLock构造方法 方法名说明ReentrantLock()创建一个ReentrantLock的实例加锁解锁方法 方法名说明void lock()获得锁void unlock()释放锁代码演示 public class SellTicket implements Runnable { private int tickets = 100; private Lock lock = new ReentrantLock(); @Override public void run() { while (true) { try { lock.lock(); if (tickets >0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票"); tickets--; } } finally { lock.unlock(); } } } } public class SellTicketDemo { public static void main(String[] args) { SellTicket st = new SellTicket(); Thread t1 = new Thread(st, "窗口1"); Thread t2 = new Thread(st, "窗口2"); Thread t3 = new Thread(st, "窗口3"); t1.start(); t2.start(); t3.start(); } }3.生产者消费者
3.1生产者和消费者模式概述【应用】
概述 生产者消费者模式是一个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻。 所谓生产者消费者问题,实际上主要是包含了两类线程: 一类是生产者线程用于生产数据 一类是消费者线程用于消费数据 为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库 生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为 消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为 [图片上传失败...(image-779763-1604492765214)] Object类的等待和唤醒方法 方法名说明void wait()导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法void notify()唤醒正在等待对象监视器的单个线程void notifyAll()唤醒正在等待对象监视器的所有线程3.2生产者和消费者案例【应用】
案例需求 生产者消费者案例中包含的类: 奶箱类(Box):定义一个成员变量,表示第x瓶奶,提供存储牛奶和获取牛奶的操作 生产者类(Producer):实现Runnable接口,重写run()方法,调用存储牛奶的操作 消费者类(Customer):实现Runnable接口,重写run()方法,调用获取牛奶的操作 测试类(BoxDemo):里面有main方法,main方法中的代码步骤如下 ①创建奶箱对象,这是共享数据区域 ②创建消费者创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作 ③对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作 ④创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递 ⑤启动线程 代码实现 public class Box { //定义一个成员变量,表示第x瓶奶 private int milk; //定义一个成员变量,表示奶箱的状态 private boolean state = false; //提供存储牛奶和获取牛奶的操作 public synchronized void put(int milk) { //如果有牛奶,等待消费 if(state) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果没有牛奶,就生产牛奶 this.milk = milk; System.out.println("送奶工将第" + this.milk + "瓶奶放入奶箱"); //生产完毕之后,修改奶箱状态 state = true; //唤醒其他等待的线程 notifyAll(); } public synchronized void get() { //如果没有牛奶,等待生产 if(!state) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //如果有牛奶,就消费牛奶 System.out.println("用户拿到第" + this.milk + "瓶奶"); //消费完毕之后,修改奶箱状态 state = false; //唤醒其他等待的线程 notifyAll(); } } public class Producer implements Runnable { private Box b; public Producer(Box b) { this.b = b; } @Override public void run() { for(int i=1; i<=30; i++) { b.put(i); } } } public class Customer implements Runnable { private Box b; public Customer(Box b) { this.b = b; } @Override public void run() { while (true) { b.get(); } } } public class BoxDemo { public static void main(String[] args) { //创建奶箱对象,这是共享数据区域 Box b = new Box(); //创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作 Producer p = new Producer(b); //创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作 Customer c = new Customer(b); //创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递 Thread t1 = new Thread(p); Thread t2 = new Thread(c); //启动线程 t1.start(); t2.start();【深入解析多线程编程技术与应用】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
我的电脑配置还行,是不是开启多线程能玩游戏更流畅?
有5位网友表示赞同!
我一直以为多线程只有电脑专业人才会了解...
有18位网友表示赞同!
我正在学习编程,听说多线程很重要哎!具体是怎样提高效率的嘛?
有10位网友表示赞同!
手机现在也支持多线程了么?可以让我玩游戏的时候更流畅点吗?
有14位网友表示赞同!
多线程技术是不是比较复杂?需要很高的技能才能掌握?
有19位网友表示赞同!
我感觉我的电脑反应变慢了,会不会是因为CPU的多线程不工作正常?
有16位网友表示赞同!
想问一下,多线程能提高软件运行速度吗?
有19位网友表示赞同!
最近学习操作系统知识,发现多线程好关键啊!
有17位网友表示赞同!
现在电脑的CPU都支持多核多线程吧?哪个品牌比较好呢?
有7位网友表示赞同!
我想买一台新的电脑,是不是要注重CPU的多线程性能?
有16位网友表示赞同!
很多大型软件都是基于多线程设计的对吗?
有7位网友表示赞同!
我学安卓开发,发现多线程是个麻烦事儿...
有15位网友表示赞同!
感觉多线程这个概念太抽象了,有什么简单的解释么?
有15位网友表示赞同!
是不是每款多核CPU的性能都一样呢?还是有一些区别?
有17位网友表示赞同!
学习多线程编程真的很难吗?有没有什么好用的教材推荐?
有10位网友表示赞同!
请问多线程可以解决哪些具体的实际问题?
有19位网友表示赞同!
我听说多线程应用很多在人工智能领域中,是吗?
有8位网友表示赞同!
这个领域的发展是不是离不开多线程技术的进步呢
有5位网友表示赞同!
希望将来电脑的多线程技术能变得更强大.
有13位网友表示赞同!