JavaSE学习总结第23天_多线程1
23.01 多线程程序的引入
如果一个程序只有一个执行流程,所以这样的程序就是单线程程序。
如果一个程序有多条执行流程,那么,该程序就是多线程程序。
23.02 进程概述及多进程的意义
要想说线程,首先必须得知道进程,因为线程是依赖于进程存在的
进程:正在运行的程序,是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。
多进程意义:多进程的作用不是提高执行速度,而是提高CPU的使用率
单进程计算机只能做一件事情。而我们现在的计算机都可以一边玩游戏(游戏进程),一边听音乐(音乐进程),所以我们常见的操作系统都是多进程操作系统。
比如:Windows,Mac和Linux等,能在同一个时间段内执行多个任务。
对于单核计算机来讲,游戏进程和音乐进程不是同时运行的,因为CPU在某个时间点上只能做一件事情,计算机是在游戏进程和音乐进程间做着频繁切换,且切换速度很快,所以,我们感觉游戏和音乐在同时进行,其实并不是同时执行的。
23.03 线程概述及多线程的意义
在一个进程内部又可以执行多个任务,而这每一个任务就可以看成是一个线程。线程是程序中单个顺序的控制流,是程序使用CPU的基本单位。
线程:
是进程中的单个顺序控制流,是一条执行路径
一个进程如果只有一条执行路径,则称为单线程程序。
一个进程如果有多条执行路径,则称为多线程程序。
多线程意义: 多线程的作用也不是提高执行速度,而是为了提高应用程序的使用率。
多个线程共享同一个进程的资源(堆内存和方法区),但是栈内存是独立的,一个线程一个栈。所以他们仍然是在抢CPU的资源执行。一个时间点上只有能有一个线程执行。而且谁抢到,这个不一定,所以,造成了线程运行的随机性。
23.04 并行和并发的区别
注意两个词汇的区别:并行和并发
并行是逻辑上同时发生,指在某一个时间内同时运行多个程序
并发是物理上同时发生,指在某一个时间点同时运行多个程序
23.05 Java程序运行原理和JVM的启动是多线程的吗
Java程序运行原理:java 命令会启动 java 虚拟机,启动 JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个“主线程”,然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。在此之前的所有程序都是单线程的。
由于JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的
23.06 实现多线程及多线程方式1的思路
由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。而进程是由系统创建的,所以我们应该去调用系统功能创建一个进程。Java是不能直接调用系统功能的,但是Java可以去调用C/C++写好的程序来实现多线程程序。由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西,然后提供一些类供我们使用。我们就可以实现多线程程序了。
Java提供的类 Thread
通过查看API,知道有2中方式实现多线程程序。
方式1:继承Thread类
步骤:
1:自定义类MyThread继承Thread类。
2:MyThread类重写run()方法
3:创建对象
4:启动线程
23.07 多线程方式1的代码实现
例:
public class Practice
{
public static void main(String[] args)
{
//创建对象
//MyThread mt = new MyThread();
//run()方法直接调用其实就相当于普通的方法调用,所以看到的是单线程的效果
//要想看到多线程的效果,就必须使用另一个方法:start()
//mt.run();
// 创建两个线程对象
MyThread my1 = new MyThread();
MyThread my2 = new MyThread(); my1.start();
my2.start(); }
}
//继承Thread类
class MyThread extends Thread
{
//重写run()方法
@Override
public void run()
{
// 一般来说,被线程执行的代码肯定是比较耗时的。所以用循环改进
for (int x = 0; x < 200; x++)
{
System.out.println(x);
}
}
}
run()和start()的区别
run():仅仅是封装被线程执行的代码,直接调用是普通方法
start():首先启动了线程,然后再由jvm去调用该线程的run()方法
继承Thread类的类为什么要重写run()方法?
不是类中的所有代码都需要被线程执行的。而这个时候,为了区分哪些代码能够被线程执行,java提供了Thread类中的run()用来包含那些被线程执行的代码。
23.08 获取和设置线程对象名称
public final String getName():返回该线程的名称
public final void setName(String name):改变线程名称,使之与参数 name 相同。
例:
public class Practice
{
public static void main(String[] args)
{
// MyThread my1 = new MyThread();
// MyThread my2 = new MyThread();
//
// //调用方法设置名称
// my1.setName("线程1");
// my2.setName("线程2");
// //启动线程
// my1.start();
// my2.start(); //带参构造方法给线程起名字
MyThread my1 = new MyThread("线程1");
MyThread my2 = new MyThread("线程2");
my1.start();
my2.start(); //获取main方法所在的线程对象的名称
//public static Thread currentThread():返回当前正在执行的线程对象
System.out.println(Thread.currentThread().getName()); }
}
class MyThread extends Thread
{ public MyThread()
{
super();
} public MyThread(String name)
{
super(name);
}
@Override
public void run()
{
for (int x = 0; x < 200; x++)
{
System.out.println(getName()+":"+x);
}
}
}
运行结果:
main
线程1:0(省略部分结果)
线程2:8(省略部分结果)
通过Thread类的getName( )方法可以获取线程的名称,默认的命名方式是:Thread-编号(从0开始)
为什么名称是:Thread-编号?
看源码:
class Thread
{
private char name[];
private static int threadInitNumber;
private static synchronized int nextThreadNum()
{
return threadInitNumber++;
}
public Thread()
{
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public final String getName()
{
return String.valueOf(name);
}
private void init(ThreadGroup g, Runnable target, String name,long stackSize)
{
init(g, target, name, stackSize, null);
} private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc)
{
//省略部分代码
this.name = name.toCharArray();
}
}
23.09 线程调度及获取和设置线程优先级
线程调度:
假如计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,线程只有得到 CPU时间片,也就是使用权,才可以执行指令。那么Java是如何对线程进行调用的呢?
线程有两种调度模型:
分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。
Java使用的是抢占式调度模型。
设置和获取线程优先级
public final int getPriority():返回线程的优先级。
public final void setPriority(int newPriority):更改线程的优先级。
例:
public class Practice
{
public static void main(String[] args)
{
MyThread my1 = new MyThread();
MyThread my2 = new MyThread();
MyThread my3 = new MyThread(); //设置优先级,最小为1,最大为10
my3.setPriority(9);
//获取优先级,默认为5
System.out.println(my1.getPriority());//
System.out.println(my2.getPriority());//
System.out.println(my3.getPriority());// my1.start();
my2.start();
my3.start();//获取的 CPU 时间片相对多一些
}
}
class MyThread extends Thread
{
@Override
public void run()
{
for (int x = 0; x < 200; x++)
{
System.out.println(getName()+":"+x);
}
}
}
23.10 线程控制之休眠线程
public static void sleep(long millis)throws InterruptedException
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。该线程不丢失任何监视器的所属权。
例:
class MyThread extends Thread
{
@Override
public void run()
{
for (int x = 0; x < 5; x++)
{
System.out.println(getName()+":"+x+":"+new Date());
try
{
//休眠1秒
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
23.11 线程控制之加入线程
public final void join()throws InterruptedException
等待该线程终止。
例:
public class Practice
{
public static void main(String[] args)
{
MyThread my1 = new MyThread();
MyThread my2 = new MyThread();
MyThread my3 = new MyThread(); my1.setName("线程1");
my2.setName("线程2");
my3.setName("线程3"); my1.start();
try
{
//等待线程1线程,线程1运行完成后,线程2线程3开始争夺资源
my1.join();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
my2.start();
my3.start();
}
}
23.12 线程控制之礼让线程
public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。
例:
class MyThread extends Thread
{
@Override
public void run()
{
for (int x = 0; x < 100; x++)
{
System.out.println(getName()+":"+x);
//暂停当前线程,并执行其他线程,不能保证机会均等
Thread.yield();
}
}
}
23.13 线程控制之守护线程
public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。
例:
public class Practice
{
public static void main(String[] args)
{
MyThread my1 = new MyThread();
MyThread my2 = new MyThread(); my1.setName("线程1");
my2.setName("线程2"); //将该线程标记为守护线程
my1.setDaemon(true);
my2.setDaemon(true); my1.start();
my2.start(); Thread.currentThread().setName("主线程");
for (int i = 0; i < 20; i++)
{
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
23.14 线程控制之中断线程
1.public void interrupt() 中断线程。
2.public final void stop() 停止线程,已过时。该方法具有固有的不安全性。
例:
public class Practice
{
public static void main(String[] args)
{
MyThread my = new MyThread();
my.setName("线程1");
my.start();
try
{
Thread.sleep(2000);
//2秒后终止my线程,使用stop()后面的语句都不会执行
//my.stop();该方法已过时,具有不安全性
my.interrupt();//后面的语句会执行
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
23.15 线程生命周期图解
23.16 多线程方式2的思路及代码实现
步骤:
1:自定义类MyRunnable实现Runnable接口
2:重写run()方法
3:创建MyRunnable类的对象
4:创建Thread类的对象,并把3步骤的对象作为构造参数传递
public class Practice
{
public static void main(String[] args)
{
// 创建MyRunnable类的对象
MyRunnable my = new MyRunnable(); // 创建Thread类的对象,并把C步骤的对象作为构造参数传递
// 构造方法 Thread(Runnable target)
// Thread t1 = new Thread(my);
// Thread t2 = new Thread(my);
// t1.setName("小明");
// t2.setName("小红"); // 构造方法 Thread(Runnable target, String name)
Thread t1 = new Thread(my, "小明");
Thread t2 = new Thread(my, "小红"); t1.start();
t2.start();
}
}
class MyRunnable implements Runnable
{
@Override
public void run()
{
for (int i = 0; i < 200; i++)
{
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
23.17 实现接口方式的好处
1.可以避免由于Java单继承带来的局限性。
2.适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。
23.18 继承Thread类的方式卖电影票案例
某电影院电影票共有100张,有3个售票窗口售票,请设计一个程序模拟该电影院售票。
public class Practice
{
public static void main(String[] args)
{
sellTicket st1 = new sellTicket();
sellTicket st2 = new sellTicket();
sellTicket st3 = new sellTicket(); st1.setName("窗口1");
st2.setName("窗口2");
st3.setName("窗口3"); st1.start();
st2.start();
st3.start(); }
}
class sellTicket extends Thread
{
// 定义100张票
// private int tickets = 100;
// 为了让多个线程对象共享这100张票,我们其实应该用静态修饰
private static int tickets = 100; @Override
public void run()
{
// 定义100张票
// 每个线程进来都会走这里,这样的话,每个线程对象相当于买的是自己的那100张票,这不合理,所以应该定义到外面
// int tickets = 100; // 是为了模拟一直有票
while (true)
{
if (tickets > 0)
{
System.out.println(getName() + "正在出售第" + (tickets--) + "张票");
}
}
}
}
23.19 实现Runnable接口的方式卖电影票案例
public class Practice
{
public static void main(String[] args)
{
SellTicket st = new SellTicket(); new Thread(st, "窗口1").start();
new Thread(st, "窗口2").start();
new Thread(st, "窗口3").start();
}
}
class SellTicket implements Runnable
{
// 定义100张票
private int tickets = 100; @Override
public void run()
{
while (true)
{
if (tickets > 0)
{
System.out.println(Thread.currentThread().getName() + "正在出售第"
+ (tickets--) + "张票");
}
}
}
}
23.20 买电影票出现了同票和负数票的原因分析
例:
class SellTicket implements Runnable
{
// 定义100张票
private int tickets = 100; @Override
public void run()
{
while (true)
{
//该语句存在安全隐患,如果当票数还剩1张的时候,此时4个线程可能
//由于CPU的随机切换而通过该语句,当执行下面的语句时就会出现负的
//票数,同时也可能出现相同的票卖出多次
if (tickets > 0)
{
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第"
+ (tickets--) + "张票");
}
}
}
}
相同的票出现多次:CPU的一次操作必须是原子性的
出现了负数的票:随机性和延迟导致的
23.21 线程安全问题的产生原因分析
线程安全问题产生的原因:
1.多个线程在操作共享的数据
2.操作共享数据的线程代码有多条
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生
23.22 同步代码块的方式解决线程安全问题
思想:把多条语句操作共享数据的代码给包成一个整体,让某个线程在执行的时候,其他线程不能来执行。Java提供了:同步机制。
同步代码块:
synchronized(对象)
{需要同步的代码;}
例:
public class SellTicket implements Runnable
{
// 定义100张票
private int tickets = 100;
//创建锁对象
private Object obj = new Object();
@Override
public void run()
{
while (true)
{
//锁对象必须是同一个
synchronized(obj)
{
if (tickets > 0)
{
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第"
+ (tickets--) + "张票");
}
}
}
}
}
23.23 同步的特点及好处和弊端
同步的前提:多个线程且多个线程使用的是同一个锁对象
同步的好处:同步的出现解决了多线程的安全问题
同步的弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
23.24 同步代码块的锁及同步方法应用和锁的问题
同步方法就是把同步关键字加到方法上
例:
//同步方法
private synchronized void sellTicket()
{
if (tickets > 0)
{
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第"
+ (tickets--) + "张票");
}
}
1.同步代码块的锁对象是任意对象
2.同步函数使用的锁是this
3.静态的同步函数使用的锁是该函数所属的字节码文件对象可以用getClass方法获取,也可以用当前类名.class表示
23.25 以前的线程安全的类回顾
Collections中让集合同步功能
例:
// public static <T> List<T> synchronizedList(List<T> list)
List<String> list1 = new ArrayList<String>();// 线程不安全
List<String> list2 = Collections.synchronizedList(new ArrayList<String>()); // 线程安全
JavaSE学习总结第23天_多线程1的更多相关文章
- JavaSE学习总结第24天_多线程2
24.01 JDK5之后的Lock锁的概述和使用 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK ...
- JavaSE学习总结第08天_面向对象3
08.01 工具类中使用静态 例: class ArrayTool { //将构造方法私有,外界不能创建该类的对象 private ArrayTool(){} //遍历数组 public stat ...
- JavaSE学习总结第09天_面向对象4
09.01 final关键字引入 例: class Fu { public final void show() { System.out.println("访问底层资源"); ...
- JavaSE学习总结第27天_反射 & 设计模式 & JDK5、7、8新特性
27.01 反射_类的加载概述和加载时机 类的加载:当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. 加载:就是指将class文件读 ...
- JavaSE学习总结第07天_面向对象2
07.01 成员变量和局部变量的区别 1.在类中的位置不同 成员变量 类中方法外 局部变量 方法内或者方法声明上 2.在内存中的位置不同 成员变量 堆内存 局部变量 栈内存 3 ...
- JavaSE学习总结第10天_面向对象5
10.01 运动员和教练案例分析 教练和运动员案例:运动员分乒乓球运动员和篮球运动员,教练分乒乓球教练和篮球教练.为了出国交流,跟乒乓球相关的人员都需要学习英语. 分析: 10.02 运动员和教练 ...
- JavaSE学习总结第11天_开发工具 & API常用对象1
11.01 常见开发工具介绍 1:操作系统自带的记事本软件 2:高级记事本软件例:Editplus,Notepad++,UltraEdit 3:集成开发环境 IDE(Integrated Deve ...
- JavaSE学习总结第26天_网络编程
26.01 网络编程概述 网络编程:就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换. 26.02 网络模型概述和图解 计算机网络之间以何种规则进行通信,就是网络模型研究问题. ...
- JavaSE学习总结第15天_集合框架1
15.01 对象数组的概述和使用 public class Student { // 成员变量 private String name; private int age; // 构造方法 publ ...
随机推荐
- VS Code - Debugger for Chrome
VS Code - Debugger for Chrome调试JavaScript的两种方式 VS Code - Debugger for Chrome调试JavaScript的两种方式 最近由于 ...
- GitHub Android 最火开源项目Top20 GitHub 上的开源项目不胜枚举,越来越多的开源项目正在迁移到GitHub平台上。基于不要重复造轮子的原则,了解当下比较流行的Android与iOS开源项目很是必要。利用这些项目,有时能够让你达到事半功倍的效果。
1. ActionBarSherlock(推荐) ActionBarSherlock应该算得上是GitHub上最火的Android开源项目了,它是一个独立的库,通过一个API和主题,开发者就可以很方便 ...
- JavaEE Tutorials (15) - 对Java持久化API应用使用二级缓存
15.1二级缓存概述190 15.1.1控制实体是否可以缓存19115.2指定缓存模式设置来提高性能192 15.2.1设置缓存获取和存储模式192 15.2.2通过编程方式控制二级缓存194
- ssm框架理解
SSM框架理解 最近两星期一直在学JavaEE的MVC框架,因为之前学校开的JavaEE课程就一直学的吊儿郎当的,所以现在真正需要掌握就非常手忙脚乱,在此记录下这段时间学习的感悟,如有错误,希望大牛毫 ...
- S3C6410嵌入式应用平台构建(一)
[2014-4/8~4/10]目前我们已经积累一定的嵌入式相关知识,对嵌入式的架构及开发过程有了大体了解,唯一缺的就是实践,通过自己的分析搭建自己的嵌入式系统.下面,我将从此处开始记录我和我同学一起分 ...
- InnoDB引擎Myslq数据库数据恢复
首先祝愿看到这片文章的你永远不要有机会用到它... 本文指针对用InnoDB引擎的Mysql数据库的数据恢复,如果是其它引擎的Mysql或其它数据库请自行google... 如果有一天你手挫不小心删掉 ...
- iOS 按钮倒计时功能
iOS 按钮倒计时功能, 建议把按钮换成label,这样会避免读秒时闪烁 __block ; __block UIButton *verifybutton = _GetverificationBtn; ...
- OOP中的多态
尽管一直在说OOP,但说实话还不是真正的理解,面向对象的三个基本特性继承.封装.多态,前两个性质曾经 有接触听的比較多还好理解,以下主要介绍一下第三个特性--多态. 1. 定义 同一操作作用于 ...
- 在基类中的析构函数声明为virtual
#include <iostream> using namespace std; class Father { public: ~Father() { cout << &quo ...
- javascript条件运算符
variablename=(condition)?value1:value2 javascript条件运算符