java-多线程的入门_进阶总结
多线程
概述图
1.概述
进程:正在执行中的程序,其实时应用程序在内存中运行的那片空间。
线程:进程中的一个执行单元,负责进程中的程序的运行,一个进程中至少要有一个线程。
(进程可以理解为是一个QQ程序,QQ运行本身就是一个线程(main),你可以在QQ上做好多事情,每个事情就相当于一个线程)
一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
程序启动了多线程,有什么应用呢?
可以实现多部分程序同时执行,专业术语称之为 并发。
1.1 并发与并行的区别?
你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。
你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。
你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。
并发的关键是你有处理多个任务的能力,不一定要同时。
并行的关键是你有同时处理多个任务的能力。
所以我认为它们最关键的点就是:是否是『同时』
1.2 同步和异步的区别?
1.3 多线程的使用可以合理使用cpu的资源,
如果线程过多会导致降低性能。
CPU处理程序时是通过快速切换完成的,在我们看来好像随机一样。
举例:金山卫士进行电脑体检的同时,还可以清理垃圾,木马查杀等,这就是多线程
总结:什么时候使用多线程技术呢?
当多部分代码需要同时执行时,就需要使用多线程技术。
1.4 线程的存在解决什么问题?
多部分代码同时执行的问题。
传统的单个主线程从头执行到尾的运行安排,当遇到较多的操作次数时,效率偏低
多线程的弊端:
开启过多会降低效率,多线程的原理其实是多个执行单元执行一个程序,CPU高速在多个线程之间进行切换,
当线程个数过多时,CPU切换一个循环的时间过长,相应的降低了程序运行的效率
多线程的特性:
随机性,因为CPU的快速切换造成的。其他总结:线程有并发性
2.创建线程
2.1 thread
1,通过原来的主线程的执行和需求做对比。
2,需求:在主线程中执行到部分代码多次无法继续执行,如何实现让下面的代码和主线程同时执行。
3,创建方式,通过查api。Thread类。
继承Thread类。
3.1 定义一个类继承Thread。
3.2 重写run方法。
3.3 创建子类对象,就是创建线程对象。
3.4 调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法。
目的让主线程和自定义线程同时执行。
class ThreadDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Ticket t1 = new Ticket(); t.start;
t1.run();
}
}
class Ticket extends Thread
{
private int tickets;
Ticket( int tickets ){
this.tickets = tickets;
}
public void run(){
if ( ticket> )
{
System.out.println(Thread.currentThread().getName()+tickets--);
}
}
}
为什么要这么做?
继承Thread类:因为Thread类描述线程事物,具备线程应该有功能。
那为什么不只讲创建Thread类的对象呢?
Thread t1 = new Thread();
t1.start();//这么做没有错,但是该start调用的时Thread类中的run方法,
而这个run方法没有做什么事情,更重要的是这个run方法中并没有定义我们需要让线程执行的代码。
创建线程的目的是什么?
是为了建立单独的执行路径,让多部分代码实现同时执行。
也就是说线程创建并执行需要给定的代码(线程的任务)。
对于之前所讲的主线程,它的任务定义在main函数中。
自定义线程需要执行的任务都定义在run方法中。
Thread类中的run方法内部的任务并不是我们所需要,只要重写这个run方法,
既然Thread类已经定义了线程任务的位置,只要在位置中定义任务代码即可。
所以进行了重写run方法动作。
多线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间。
进行方法的压栈和弹栈。
当执行线程的任务结束了,线程自动在栈内存中释放了。
但是当所有的执行线程都结束了,那么进程就结束了。
2.2 体会调用run和调用start的区别?
调用run方法仅仅是主线程执行run方法中的代码;调用start方法是开启线程,让新建的线程与主线程同时执行
其他总结:注意在这个程序中,run()方法是中没有循环的;没有创建线程就不能执行;一个线程只有调用了start()方法才能开始线程,
3.创建线程thread的原理
1,为什么要继承Thread,
因为Thread类描述了线程的任务存在的位置:run方法。
2,为什么要覆盖run方法。
为了定义线程需要运行的任务代码。
3,为什么调用start方法。
为了开启线程,并运行子类的run方法的任务代码。
4,创建线程的目的是什么?
为了执行线程任务,而是任务都定义在run方法中。
run方法结束了,线程任务就是结束了,线程也就是结束了。
5,线程任务:每一个线程都有自己执行的代码,主线程的代码都定义在主函数中,自定义线程的代码都定义在run方法中。
3.1 多线程的运行原理
1,疑问,如果多部分代码同时执行,那么在栈内存方法执行是怎么完成的呢?
其实是,每一个线程在栈内存中都有自己的栈内存空间,在自己的栈空间中
进行压栈和弹栈。
2,主线程结束,程序就结束吗?
主线程结束,如果其他线程还在执行,进程是不会结束了,当所有的线程都结束了,进程才会结束。
3,画出多线程的运行内存图。
3.2 异常在多线程中的特点
throw异常是可以结束功能。
如果功能是线程的任务,那么功能结束就代表着线程结束。
而且异常信息中会体现出该异常在哪个线程上发生。
Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: 4
at Demo.run(ThreadDemo.java:68)
4,创建线程实现Runnable接口
1,通过查阅api得到第二种方式
2,不明确原理的情况下,先应用。
3,方式二步骤:
3.1,定义类实现Runnable接口:
3.2,覆盖接口中的run方法。
3.3,创建Thread类的对象:
3.4,将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
3.5,调用Thread类的start方法开启线程。
到这里,你就应该可以使用方式二创建线程了。动手自己通过方式二完成线程的创建并运行。★★★★★
4,方式二的原理:★★★
4.1,定义类实现Runnable接口:避免了继承Thread类的单继承局限性。
4.2,覆盖接口中的run方法。将线程任务代码定义到run方法中。
4.3,创建Thread类的对象:只有创建Thread类的对象才可以创建线程。
4.4,将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
因为线程已被封装到Runnable接口的run方法中,而这个run方法所属于Runnable接口的子类对象,
所以将这个子类对象作为参数传递给Thread的构造函数,这样,线程对象创建时就可以明确要运行的线程的任务。
4.5,调用Thread类的start方法开启线程。
5,方式二和方式一的区别:【面试题】★★★★★
5.1 第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。
5.2 实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。
继承Thread类:线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。
实现runnable接口:将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。
Runnable接口对线程对象和线程任务进行解耦。
查询到的源码解释
class Thread{ private Runnable target; Thread(Runnable target)
{
this.target = target;
}
public void run() {
if (target != null) {
target.run();
}
}
public void start()
{
run();
}
} Runnable d = new Demo();
Thread t = new Thread(d);
t.start();
5,多线程的售票案例
案例:售票的例子。
售票的动作需要同时执行,所以使用多线程技术。
class Tickets implements Runnable{
private int tickets = ; public void run() {
while(tickets > )
System.out.println(Thread.currentThread().getName()+" "+tickets--); } public static void main(String[] args) {
Tickets t = new Tickets();
Thread t1 = new Thread(t,"t1");
Thread t2 = new Thread(t,"t2");
Thread t3 = new Thread(t,"t3");
Thread t4 = new Thread(t,"t4");
t1.start();
t2.start();
t3.start();
t4.start(); }
6,线程的状态
1,线程运行是有多种状态的
1.1 创建 new Thread类或者其子类对象。
1.2 运行 start(). 具备者CPU的执行资格和CPU的执行权。
1.3 冻结 释放了CPU的执行资格和CPU的执行权。比如执行到sleep(time) wait() 导致线程冻结。
1.4 临时阻塞状态: 具备者CPU的执行资格,不具备CPU的执行权。
1.5 消亡:线程结束。run方法结束。
举例:老师给几个小朋友喂饭
8 多线程的安全问题
分析上面的案例 (多个线程共享一个资源)
我下倒水呢,刚到一半(if语句判断后,不去打印了),我离开了(不去打印了),下一个(另一个线程)来了
把我里面水喝了,(tickets--),我回来了,我这时不用在判断了,直接打印tickets--(但是ticktes 已经减了一次了)不合理的
案例:售票的示例。
1,通过案例让安全问题发生。
2,安全问题产生的原因:
2.1 线程任务中有共享的数据。
2.2 线程任务中有多条操作共享数据的代码。
当线程在操作共享数据时,其他线程参与了运算导致了数据的错误(安全问题)
3,安全问题的解决思路:
保证在线程执行共享数据代码时,其他线程无法参与运算。
4,安全问题的解决代码体现:
同步代码块。synchronized(obj){需要被同步的代码} 【举例:火车上的卫生间】
5,同步的好处:
解决了安全问题。
6,同步的弊端:
降低了程序的性能。
7,同步的前提:
必须保证多个线程在同步中使用的是同一个锁。
解决了什么问题?
当多线程安全问题发生时,加入了同步后,
问题依旧,就要通过这个同步的前提来判断同步是否写正确。
其他总结:同步里面的对象相当于火车上卫生间的锁,火车上就你自己一个人(一个线程),你去卫生间不用锁,3号卫生间的锁和4号卫生间的锁是没有关系的.因此多个线程要用同一个锁.
使用同步解决上面的问题
class Ticket implements Runnable
{
//1,描述票的数量。
private int tickets = ;
//2,售票的动作,这个动作需要被多线程执行,那就是线程任务代码。需要定义run方法中。
//线程任务中通常都有循环结构。
private Object obj = new Object();
public void run()
{
while(true)
{
synchronized(obj)
{
if(tickets>)
{
//要让线程在这里稍停,模拟问题的发生。sleep 看到了0 -1 -2 错误的数据,这就是传说中的多线程安全问题。
try{Thread.sleep();}catch(InterruptedException e){/*未写处理方式,后面讲*/} System.out.println(Thread.currentThread().getName()+"....."+tickets--);//打印线程名称。
}
}
}
}
}
class ThreadDemo3
{
public static void main(String[] args)
{
//1,创建Runnable接口的子类对象。
Ticket t = new Ticket(); //2,创建四个线程对象。并将Runnable接口的子类对象作为参数传递给Thread的构造函数。
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t); //3,开启四个线程。
t1.start();
t2.start();
t3.start();
t4.start();
}
}
其他总结:同步里面的对象相当于火车上卫生间的锁,火车上就你自己一个人(一个线程),你去卫生间不用锁,3号卫生间的锁和4号卫生间的锁是没有关系的.因此多个线程要用同一个锁.
习题
//写出下面代码执行的结果(此题需写出分析过程) class MyThread extends Thread{
public void run(){
try {
Thread.currentThread().sleep();
} catch (InterruptedException e) {
}
System.out.println("MyThread running");
}
} public class ThreadTest{
public static void main(String argv[]) {
MyThread t = new MyThread();
t.run();
t.start();
System.out.println("Thread Test");
}
}
执行结果:
MyThread running
Thread Test
MyThread running
过程分析:
1,MyThread t = new MyThread();
创建线程对象,但并未开启。
2,t.run();
是主线程执行t对象的run方法。
在run方法中,主线程执行到sleep(3000);主线程就处于冻结状态,
3秒后,主线程恢复到运行状态,就打印了"MyThread running"。
3,t.start();
主线程执行该句,开启了一个新的线程Thread-0。
这时程序中有两个线程,执行出现了两种情况:
情况一:主线程执行完t.start()方法后,继续执行打印语句,输出"Thread Test"。
然后新线程Thread-0执行run方法,并sleep(3000)3秒,3秒后,执行"MyThread running"
情况二:主线程执行完t.start()方法后,开启了一个新线程Thread-0,
该新线程Thread-0就开始执行,调用run方法,sleep(3000)3秒,这时主线程开始执行,
打印了"Thread Test",主线程结束,3秒后,新线程Thread-0打印"MyThread running"。
//两个客户到一个银行去存钱,每个客户一次存100,存3次。
//问题:该程序是否有安全问题,如果有,写出分析过程,并定义解决方案。
class Bank
{
private int sum;
public void add(int num)
{
sum = sum + num;
System.out.println("sum="+sum);//每存一次,看到银行金额变化。
}
}
class Consumer implements Runnable
{
private Bank b = new Bank();
public void run()
{
for(int x= ; x<; x++)
{
b.add();//一次存100.循环3次,
}
}
}
class Test
{
public static void main(String[] args)
{
Consumer c = new Consumer();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start(); }
}
分析依据:多线程安全问题产生的原因:
1,共享数据,b对象中的sum。
2,操作共享数据的多次运算。
sum = sum + num;
System.out.println("sum="+sum);
java-多线程的入门_进阶总结的更多相关文章
- Java 服务端入门和进阶指南
作者:谢龙 链接:https://www.zhihu.com/question/29581524/answer/44872235 来源:知乎 著作权归作者所有,转载请联系作者获得授权. 现在互联网上资 ...
- 微博,and java 多线程编程 入门到精通 将cpu 的那个 张振华
http://down.51cto.com/data/2263476 java 多线程编程 入门到精通 将cpu 的那个 张振华 多个用户可以同时用一个 vhost,但是vhost之间是隔离的. ...
- java 多线程 快速入门
------------恢复内容开始------------ java 多线程 快速入门 1. 进程和线程 什么是进程? 进程是正在运行的程序它是线程的集合 进程中一定有一个主线程 一个操作系统可以有 ...
- 《day14---多线程入门_进阶》
/* 多线程: 进程:正在执行中的程序,一个应用程序启动后在内存中运行的那片空间.进程具有动态性和并发性. 线程:进程中的一个执行单元.负责进程中的程序的运行的.一个进程中至少要有一个线程. 一个进程 ...
- java多线程快速入门(六)
多线程应用实例(批量发送短信) 1.创建实体类 package com.cppdy; public class UserEntity { private int id; private String ...
- java多线程快速入门(一)
1.什么是进程 比如:QQ.QQ游戏.eclipse都是进程,可以通过任务管理器查看进程 2.进程和线程区别 线程是进程的一部分,一个进程可以包含多个线程,一个线程只能属于一个进程 进程是所有线程的集 ...
- java多线程面试题_线程并发面试题
1.什么是线程?线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速.比如,如果一个线程完成一个 ...
- java多线程快速入门(二十二)
线程池的好处: 避免我们过多的去new线程,new是占资源的(GC主要堆内存) 提高效率 避免浪费资源 提高响应速度 作用:会把之前执行某个线程完毕的线程不会释放掉会留到线程池中给下一个调用的线程直接 ...
- java多线程快速入门(二十一)
CountDownLatch(闭锁)计数器 有一个任务A,它要等待其他4个任务执行完毕之后才执行,此时就可以利用CountDownLatch来实现这种功能 package com.cppdy; imp ...
随机推荐
- IOS学习笔记56-IOS7状态栏适配方法一
近期由于IOS7的发布,所以应用的适配潮可谓是都搞的锣鼓喧天,甚是热闹,因此呢,因适配IOS7而产生的问题也是铺天盖地的卷来,所以了,我也从简单的状态栏适配开始,先研究了下关于状态栏的适配,特总结如下 ...
- vue + element-ui实现简洁的导入导出功能
1.安装ElementUI模块 cnpm install element-ui -S 2.在main.js中引入 import ElementUI from 'element-ui' import ' ...
- zuul隔离机制
文章转载自:https://blog.csdn.net/farsight1/article/details/80078099 ZuulException REJECTED_SEMAPHORE_EXEC ...
- OpenCASCADE动画功能2
OpenCASCADE动画功能2 eryar@163.com OpenCASCADE是一个开发平台,主要提供三维曲面和实体建模.CAD数据交换及可视化等功能.OCCT最适用于开发三维建模CAD软件.加 ...
- Delphi版俄罗斯方块-前奏
前言 基础知识讲了很多,但是并没有串联起来,所以我最近一直在准备个小项目,但是这个项目的要求不含有数据库部分,也就是数据持久存储的功能,此外不能含有网络功能,它只是对基础知识的一个总结,最后一点是这个 ...
- Python学习day26-面向对象之小结
figure:last-child { margin-bottom: 0.5rem; } #write ol, #write ul { position: relative; } img { max- ...
- Spring Cloud Alibaba 使用Sentinel实现接口限流
Sentinel是什么 Sentinel的官方标题是:分布式系统的流量防卫兵.从名字上来看,很容易就能猜到它是用来作服务稳定性保障的.对于服务稳定性保障组件,如果熟悉Spring Cloud的用户,第 ...
- sql语句怎么看效率?
1.数据库设计当面: 对查询进行优化,应该尽量避免全表扫描,首先应考虑在where及order by设计的列上加索引. d.索引并不是越多越好,索引可以提高查询效率,同时降低了insert和updat ...
- goland破解
PyCharm是由著名的JetBrains公司所打造的一款功能强大的Python IDE,它具有一般IDE都具备的功能,并且使用起来非常方便好用.最近需求PyCharm激活码的网友非常多,小编就在这里 ...
- HZOI20190906模拟39 工业,卡常,玄学
题面:https://www.cnblogs.com/Juve/articles/11484209.html 工业: 推一个式子,AC 没有用组合数....推了2个多小时 我sbsbsbsbsbsbs ...