java学习笔记(6)-多线程(1)
标签(空格分隔): 笔记
一、基本概念###
1.1 程序、进程、线程
- 程序(program):是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
- 进程(process):是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期
- 如运行中的QQ,运行中的MP3播放器
- 程序是静态的,进程是动态的
- 进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
- 线程(thread):,进程可进一步细化为线程,是一个程序内部的一条执行路径。
- 若一个进程同一时间并行执行多个线程,就是支持多线程的
- 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
- 一个进程中的多个线程共享相同的内存单元/内存地址空间--->它们从同一堆中分配对象,可以 访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资 源可能就会带来安全的隐患。
1.2 并行与并发
- 并行:许多cpu同时执行多个任务
- 并发:一个cpu同时执行多个任务
1.3 使用多线程的优点
- 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
- 提高计算机系统CPU的利用率
- 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改
1.4 何时需要多线程
- 程序需要同时执行两个或多个任务。
- 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
- 需要一些后台运行的程序时。
二、线程的创建和使用###
1.继承Thread类,然后重写run函数,不需要直接调用他,这个函数是JVM自动调用的。
public class MyThread1Test extends Thread{
@Override
public void run() {
for(int i=0;i<2000;i++) {
System.out.println("Threa666666666666");
}
}
}
2.实现Runnable接口,重写run函数,同理也不需要调用这个run函数,如果调用了系统就会把它当成普通函数。
public class MyRunnableTest implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
for(int j=0;j<2000;j++) {
System.out.println("Runnable********执行");
}
}
}
3.调用上述两个方法(普通外部类),启动多线程
Thread t1=new MyThread1Test();
t1.start();//
Thread t2=new Thread(new MyRunnableTest());
t2.start();
4.通过匿名内部类实现
new Thread() {
public void run() {
for(int i=0;i<500;i++) {
System.out.println("Threa666666666666");
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
for(int j=0;j<500;j++) {
Thread.yield();
System.out.println("Runnable********执行");
}
}
}).start();
5.通过成员内部类实现
public class th3 extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<2000;i++) {
System.out.println("Threa666666666666");
}
}
}//把这个类放在一个类的内部,然后调用,调用过程略,之后直接start就行。
6.注意点:
- 如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。
- run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU 调度决定。
- 想要启动多线程,必须调用start方法。
- 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上 的异常“IllegalThreadStateException”。
7.线程的调度
调度策略
- 时间片策略
- 抢占式:高优先级的线程抢占CPU ,可以设置优先级,默认是5,自己可以设置1-10,数字越大优先级越高,但是并不意味着优先级越高就一定会先执行,只是先执行的概率要大一点
Java的调度方法
- 同优先级线程组成先进先出队列(先到先服务),使用时间片策略
- 对高优先级,使用优先调度的抢占式策略
8.线程的分类
- 用户线程
- 守护线程,(例如java的垃圾回收)是伴随着用户线程的存活而存活 当用用户线程死亡的时候 守护线程不论是否执行结束 都会死亡(不会立即死亡,而是运行一段时间再死亡),调用的时候在start前声明setDaemon(true)
- 它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。
三、线程的生命周期###
- 新建:当创建了一个线程之后 new Thread 则当前线程就处于新建状态
- 就绪:当调用了线程的start方法 则线程就进入到就绪状态 准备着与其他线程抢夺CPU的执行权
- 运行:当线程获得CPU的执行全 处于执行中的线程 就是运行状态
- 阻塞:线程在执行过程中,因为某些原因 失去了CPU的执行权 暂时被挂起 但是线程还没有结束 则线程就处于阻塞状态
- 死亡:线程任务执行完成 或者被 强制终止 则线程进入到死亡状态
线程的五种状态时可以相互转换的
四、线程的同步###
例如:使用多线程模拟火车站售票程序,开启三个窗口售票。
public class Tick implements Runnable{
private static int num = 100;
@Override
public void run() {
while(true) {
if(num > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"销售车票座位号为:" + num--);
}else {
break;
}
}
}
}
class TicketDemo {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.setName("t1窗口");
t2.setName("t2窗口");
t3.setName("t3窗口");
t1.start();
t2.start();
t3.start();
}
}
备注:上述代码会出现很多问题,比如会出现卖的票数编号重复,甚至出现负数编号的车票,例如当票数只剩一张的时候,一个线程执行,判断票数大于零之后进入了休眠状态,此时二号线程也进入了,因为一号线程休眠中,并未对票数进行修改,所以二号线程判断票数也是大于0,依次类推,当这些线程依次休眠结束后,自然会产生0,-1,-2这些不符合常理的票数编号。
- 上述问题的原因: 当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有 执行完,另一个线程参与进来执行。导致共享数据的错误。
- 解决办法: 对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。这就叫线程同步
1.线程同步实现
1.1同步代码块
同步代码块用synchronized这个关键字去实现,注意实现的时候不要和static搭配。同步代码块可以放在静态方法中和非静态方法中。
放在非静态方法中,此时的锁对象可以是任意对象 只需要保证所有的线程使用的是同一个锁对象 this也可以充当锁对象,但是必须提前创建好这个对象,如下所示
//------------------------------------------------------------
public class PrintTest {
public static void main(String[] args) {
PrintTest p1=new PrintTest();//如果可以将此行注释掉,然后把下面的两行注释解除,在看看执行效果。
new Thread() {
@Override
public void run() {
// PrintTest p1=new PrintTest();//解除此行注释看看效果
p1.print1();
}
}.start();
//----------------------------
new Thread() {
@Override
public void run() {
// PrintTest p2=new PrintTest();////解除此行注释看看效果,但要把下面的p1换成p2.
p1.print2();
}
}.start();
}
//----------------------------------
public void print1() {
while(true) {
synchronized(this) {
System.out.print("中");
System.out.print("国");
System.out.print("科");
System.out.print("技");
System.out.print("大");
System.out.print("学");
System.out.println();
}
}
}
//------------------------------------
public void print2() {
while(true) {
synchronized(this) {
System.out.print("北");
System.out.print("大");
System.out.print("青");
System.out.print("鸟");
System.out.println();
}
}
}
}
静态代码块放在静态方法中 可以使用静态对象来作为锁对象,例如字符串,本类的class对象,保证所有的线程使用的是同一个锁对象
public class PrintTest {
public static void main(String[] args) {
new Thread() {
@Override
public void run() {
print1();
}
}.start();
new Thread() {
@Override
public void run() {
print2();
}
}.start();
}
public static void print1() {
while(true) {
synchronized(PrintTest.class) {
System.out.print("中");
System.out.print("国");
System.out.print("科");
System.out.print("技");
System.out.print("大");
System.out.print("学");
System.out.println();
}
}
}
public static void print2() {
while(true) {
synchronized(PrintTest.class) {
System.out.print("北");
System.out.print("大");
System.out.print("青");
System.out.print("鸟");
System.out.println();
}
}
}
}
对于非静态同步方法 锁对象为this,可以通过下面的同步代码块和非静态同步方法证明
public class PrintTest {
public static void main(String[] args) {
PrintTest p1=new PrintTest();
new Thread() {
@Override
public void run() {
p1.print1();
}
}.start();
new Thread() {
@Override
public void run() {
while(true) {
p1.print2();
}
}
}.start();
}
//处于非静态方法中的同步代码块
public void print1() {
while(true) {
synchronized(this) {
System.out.print("中");
System.out.print("国");
System.out.print("科");
System.out.print("技");
System.out.print("大");
System.out.print("学");
System.out.println();
}
}
}
//非静态同步方法
public synchronized void print2() {
System.out.print("北");
System.out.print("大");
System.out.print("青");
System.out.print("鸟");
System.out.println();
}
}
对于静态同步方法 锁对象为字节码对象 ,本类的class对象,可以通过下面来验证:
public class PrintTest02 {
public static void main(String[] args) {
PrintTest02 p2=new PrintTest02();
Thread t1=new Thread() {
@Override
public void run() {
while(true) {
print1();
}
}
};
Thread t2=new Thread() {
public void run() {
while(true) {
p2.print2();
}
}
};
t2.start();
t1.start();
}
//静态同步方法
public static synchronized void print1() {
System.out.print("中");
System.out.print("国");
System.out.print("科");
System.out.print("技");
System.out.print("大");
System.out.print("学");
System.out.println();
}
//下面这个函数加不加staic结果都一样
public void print2() {
while(true) {
synchronized(PrintTest02.class) {
System.out.print("北");
System.out.print("大");
System.out.print("青");
System.out.print("鸟");
System.out.println();
}
}
}
}
java学习笔记(6)-多线程(1)的更多相关文章
- 【原】Java学习笔记032 - 多线程
package cn.temptation; public class Sample01 { public static void main(String[] args) { /* * [进程]:正在 ...
- Java学习笔记之——多线程
多线程编程 程序: 进程:一个程序运行就会产生一个进程 线程:进程的执行流程,一个进程至少有一个线程,称为主线程 如:QQ聊着天,同时在听音乐 一个进程可以有多个线程,多个线程共享同一个进程的资源 线 ...
- Java学习笔记:多线程(一)
Java中线程的五种状态: 新建状态(New) 就绪状态(Runnable) 运行状态(Running) 阻塞状态(Blocked) 凋亡状态(Dead) 其中阻塞状态(Blocked)又分为三种: ...
- java学习笔记(5)多线程
一.简介(过段时间再写,多线程难度有点大) --------------------------------------- 1.进程:运行时的概念,运行的应用程序 2.线程:应用程序内部并发执行的代码 ...
- Java 学习笔记(11)——多线程
Java内部提供了针对多线程的支持,线程是CPU执行的最小单位,在多核CPU中使用多线程,能够做到多个任务并行执行,提高效率. 使用多线程的方法 创建Thread类的子类,并重写run方法,在需要启动 ...
- Java学习笔记:多线程(二)
与线程生命周期相关的方法: sleep 调用sleep方法会进入计时等待状态,等待时间到了,进入就绪状态. yield 调用yield方法会让别的线程执行,但是不确保真正让出.较少使用,官方注释都说 ...
- 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...
- Java学习笔记-多线程-创建线程的方式
创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...
- 《Java学习笔记(第8版)》学习指导
<Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...
- 20145330第六周《Java学习笔记》
20145330第六周<Java学习笔记> . 这周算是很忙碌的一周.因为第六周陆续很多实验都开始进行,开始要准备和预习的科目日渐增多,对Java分配的时间不知不觉就减少了,然而第十和十一 ...
随机推荐
- 《挑战30天C++入门极限》C++类静态数据成员与类静态成员函数
C++类静态数据成员与类静态成员函数 在没有讲述本章内容之前如果我们想要在一个范围内共享某一个数据,那么我们会设立全局对象,但面向对象的程序是由对象构成的,我们如何才能在类范围内共享数据呢? ...
- Error instantiating class cn.edu.zju.springmvc.pojo.Items with invalid types () or values (). 报错解决方法
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.Reflecti ...
- 【CSP模拟赛】天才绅士少女助手克里斯蒂娜(线段树&读入优化&输出优化)
题面描述 红莉栖想要弄清楚楼下天王寺大叔的显像管电视对“电话微波炉(暂定)”的影响.选取显像管的任意一个平面,一开始平面内有个n电子,初始速度分别为vi,定义飘升系数为 $$\sum_{1\leqsl ...
- 判断qq浏览器和uc浏览器?
判断在iphone手机上打开的是uc浏览器还是qq浏览器 <html lang="en"> <head> <meta charset="ut ...
- 数据库blob图片文件,多图片打包下载
数据库存储blob图片文件,前端打包下载 数据库图片文件实体类 package com.cmrh.mspserver.pos.dto; import java.io.Serializable; imp ...
- 如何画好ER图
快速阅读 了解ER图的基本组成,以及如何在viso中画ER图. 什么是ER图 是实体关系图,用矩形表示实体,用椭圆形表示属性,用棱形表示两实体之间的联系.相互用直接联接起来,是一种数据建模工具.用来描 ...
- 各种推导式 详情见EVA_J的博客
#[每一个元素或者是和元素相关的操作 for 元素 in 可迭代数据类型] #遍历之后挨个处理 #[满足条件的元素相关的操作 for 元素 in 可迭代数据类型 if 元素相关的条件] #筛选功能 # ...
- 第10组 Alpha冲刺(3/6)
链接部分 队名:女生都队 组长博客: 博客链接 作业博客:博客链接 小组内容 恩泽(组长) 过去两天完成了哪些任务 描述 对推送模块进行详细划分 基于用户的协同过滤,寻找更感兴趣的话题 学习API文档 ...
- 进入tomcat6的控制台
在tomcat文件夹找到conf文件夹中的tomcat-user.xml文件, 用记事本打开,在最下面可以看到tomcat默认把用户注释掉了,也就是说打开tomcat主页是进不去管理页面的. 方法 ...
- Linux系列 | Ubuntu 各版本号和名称对照【转】
转载处:https://blog.csdn.net/songfulu/article/details/85310273 版本 开发代号 中译 发布日期 支持结束时间 内核版本 桌面版 服务器版 4 ...