java中的多线程 // 基础
java 中的多线程
简介
进程 : 指正在运行的程序,并具有一定的独立能力,即 当硬盘中的程序进入到内存中运行时,就变成了一个进程
线程 : 是进程中的一个执行单元,负责当前程序的执行。线程就是CPU通向程序的路径
一个进程中只有一个线程,单线程程序
一个进程中是可以有多个线程的,这个应用程序是多线程程序
程序的运行分类
分时调度
所有线程轮流使用CPU 的使用权,平均分配每个线程占用CPU 的时间
抢占式调度
优先让优先级高的线程使用CPU,如果线程的优先级相同,那么就会随机选择一个线程(线程的随机性)。
java 使用的为抢占式调度
抢占式调度简介:
现在的操作系统都支持多进程并发运行,比如:一边用office ,一边使用QQ,一边看着视频 等等,
看着好像这些程序都在同一时刻运行,实际上是 CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。
对于CPU的一个核而言,某个时刻只能执行一个线程,而CPU 在多个线程之间切换的速度相对我们而言感觉要快,看上去就是在同一时刻运行。
注意:
多线程并不能提高程序的运行速度,但能够提高程序的运行效率,让CPU 的使用效率更高。
多线程的由来
jvm启动后,必然有一个执行线程(路径)从main方法开始的,一直执行到main方法结束,这个线程在java中称之为主线程(main线程)。
若主线程遇到循环,并循环次数较多,则导致程序在指定的位置停留时间过长,无法马上执行下面的程序,则需要等待循环结束后才能够执行代码。效率慢。
主线程负责执行其中的一个循环,由另一个线程执行另一个循环,最终实现多部分代码同时执行。多线程之间互不影响。
多线程的创建方式
1、继承 Thread 类
创建一个类,继承 Thread 类,并重写Thread 类的 run 方法
创建对象,调用 run 方法 ,就相当于执行其他线程的 main 方法。
步骤:
1、自定义一个类,继承Thread 类
2、重写Thread 类中的 run 方法 ,设置线程任务
3、创建自定义类的实例
4、实例化 Thread 类,并传入自定义的类
4、调用start ,开启进程。
示例:
1、自定义类
// 创建一个类,继承Thread
public class Thread_01 extends Thread{
// 重写run 方法,在run方法中定义线程任务
public void run(){
System.out.println(Thread.currentThread().getName());
}
}
2、main方法
public class ThreadDemo {
public static void main(String[] args) {
// 创建线程对象 t1
Thread_01 t1 = new Thread_01();
// 为了启动Thread_01 这个线程,需要实例化Thread,并传入自己的Thread_01实例
Thread thread = new Thread(t1);
// 通知CPU 要启动线程
thread.start();
System.out.println(Thread.currentThread().getName());
}
}
多线程继承Thread 示例
2、实现 Runnable 接口
创建一个类,实现 Runnable 接口,重写 run 方法
步骤:
1、自定义一个类,实现 Runnable 接口
2、重写run 方法,在run方法中设置线程任务
3、在main 方法中,创建自定义类的实例化
4、实例化Thread 类,并传入自定义类的实例化
5、调用start 方法,开启进程
1、自定义类,实现runnable 接口
// 自定义类,实现Runnable 接口
public class Runnable_01 implements Runnable{
// 重写run 方法,在run方法中设置线程任务
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
} } 2、在main方法中,调用
public class ThreadDemo {
public static void main(String[] args) {
// 创建线程对象 t1
Runnable_01 t1 = new Runnable_01();
// 为了启动Runnable_01 这个线程,需要实例化Thread,并传入自己的Runnable_01实例
Thread thread = new Thread(t1);
// 通知CPU 要启动线程
thread.start();
System.out.println(Thread.currentThread().getName());
}
}
多线程实现 Runnable 接口
注意:
调用 start 方法,开启新线程。若没有调用start 方法,只调用run 方法,只是调用了一个方法而已,并没有开启新线程
一个线程只能调用一次start 方法,若线程执行结束后,不能再调用。
常用API
voidstart() 使该线程开始执行;Java 虚拟机调用该线程的run方法。staticThreadcurrentThread() 返回对当前正在执行的线程对象的引用string getName() 返回该线程的名称long getId() 返回该线程的标识符int getPriority() 返回线程的优先级Thread.State getState() 返回该线程的状态void interrupt() 中断线程void setName(String name) 改变线程名称,使之与参数name相同void setPriority(int newPriority)更改线程的优先级static voidsleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)static voidyield() 暂停当前正在执行的线程对象,并执行其他线程
public class Demo {
public static void main(String[] args) {
// 调用currentThread().getName(),获取当前线程的名称
System.out.println(Thread.currentThread().getName() + "123");
// 调用currentThread().getId(),获取当前线程的标识符
System.out.println(Thread.currentThread().getId());
// 调用currentThread().getPriority(),获取当前线程的优先级
System.out.println(Thread.currentThread().getPriority());
// 调用currentThread().setName() 给当前线程设置新名称
Thread.currentThread().setName("线程新名称");
// 调用currentThread().getName(),获取当前线程的名称
System.out.println(Thread.currentThread().getName() + "123");
/**
* 打印结果 :main123
* 1
* 5
* 线程新名称123
*/
}
}
线程API 使用示例
线程安全
若多线程调用全局变量时,会出现线程安全问题。
即:使用java 模拟窗口卖票时,一个窗口就是一个线程,若同时卖票,可能会出现几个窗口同时卖一张票,或者卖出不存在的票(就剩一张票时,两个窗口同时卖出)
所以,使用多线程时,要注意线程安全问题,解决线程安全问题有三种方式,
方式一:同步代码块
同步代码块:就是在方法块声明上加上 synchronized
synchronized (锁对象) {
可能会产生线程安全问题的代码
}
注意:
同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。
对象可以是this, 哪个对象调用,方法中的this就是哪个对象
示例:
/*
* 开启3个线程,同时卖100张票
*/
public class Demo01PayTicket {
public static void main(String[] args) {
//创建接口的实现类对象
RunnableImpl r = new RunnableImpl();
//创建线程对象
Thread t0 = new Thread(r);
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
//开启多线程
t0.start();
t1.start();
t2.start();
}
} /*
* 发现程序出现了安全问题:卖出了重复的票和不存在的票
*
* 多线程安全问题的第一种解决方案:使用同步代码块
*
* 注意:
* 代码块中传递的锁对象必须保证唯一,多个线程使用的是同一个锁对象
* 锁对象可以是任意的对象
*
*/
public class RunnableImpl implements Runnable{ //定义一个共享的票源
private int ticket = 100;
//创建一个锁对象
Object obj = new Object(); @Override
public void run() {
//让卖票重复执行
while(true){
//同步代码块
synchronized (obj) {
//判断是否还有票
if(ticket>0){ //提高安全问题出现的概率,增加一个sleep
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
} //进行卖票
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}
}
}
}
}
多线程 同步代码块
方式二:同步方法
1、同步方法:在方法声明上加上 synchronized
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步方法中的锁对象是 this,哪个对象调用,方法中的this就是哪个对象
2、静态同步方法:在方法声明上加上 static synchronized
public static synchronized void method(){
可能会产生线程安全问题的代码
}
静态同步方法中的锁对象不是对象,是本类的 .class 文件
示例:
/*
* 开启3个线程,同时卖100张票
*/
public class Demo01PayTicket {
public static void main(String[] args) {
//创建接口的实现类对象
RunnableImpl r = new RunnableImpl();
//创建线程对象
Thread t0 = new Thread(r);
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
//开启多线程
t0.start();
t1.start();
t2.start();
}
} /*
* 发现程序出现了安全问题:卖出了重复的票和不存在的票
*
* 多线程安全问题的第二种解决方案:使用同步方法
*
* 实现步骤:
* 1.把访问了共享数据的代码提取出来放在一个方法中
* 2.在方法上添加一个synchronized修饰符
*
* 格式:
* 修饰符 synchronized 返回值类型 方法名(参数){
* 访问了共享数据的代码;
* }
*
* 把选中的代码提取到方法中快捷键:alt+shift+m
*
*/
public class RunnableImpl implements Runnable{ //定义一个共享的票源
private static int ticket = 100; @Override
public void run() {
//让卖票重复执行
while(true){
payTicketStatic();
} } /*
* 静态的同步方法,锁对象不是this
* 静态优先于非静态加载到内存中,this是创建对象之后才有的
* 锁对象是本类的class属性(反射-->class文件对象)
*/
public static synchronized void payTicketStatic() {
synchronized (RunnableImpl.class) {
//判断是否还有票
if(ticket>0){
//提高安全问题出现的概率,增加一个sleep
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//进行卖票
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}
}
} /*
* 定义一个卖票的方法
* 使用synchronized修饰
* 使用锁对象把方法锁住
* 这个锁对象是谁?
* 创建的实现类对象new RunnableImpl();
* 也就是this,哪个对象调用的方法,方法中的this就是哪个对象
*/
public synchronized void payTicket() {
//System.out.println(this);//cn.itcast.demo08.RunnableImpl@bcda2d
synchronized (this) {
//判断是否还有票
if(ticket>0){
//提高安全问题出现的概率,增加一个sleep
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
} //进行卖票
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}
}
}
}
多线程 同步方法 synchronized
方式三:使用lock 锁
Lock 是接口, ReentrantLock 是Lock 的实现类
API

调用
1、创建ReentrantLock 对象
2、在可能产生安全问题代码前调用 lock() 方法,获得锁
3、调用unlock()方法,解锁
ReentrantLock rl = new ReentrantLock();
//获得锁
rl.LOCK
可能会产生线程安全问题的代码
Rl.unlock
示例:
/*
* 开启3个线程,同时卖100张票
*/
public class Demo01PayTicket {
public static void main(String[] args) {
//创建接口的实现类对象
RunnableImpl r = new RunnableImpl();
//创建线程对象
Thread t0 = new Thread(r);
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
//开启多线程
t0.start();
t1.start();
t2.start();
}
} /*
* 发现程序出现了安全问题:卖出了重复的票和不存在的票
*
* 多线程安全问题的第三种解决方案:使用Lock锁
* java.util.concurrent.locks.Lock接口
* Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
* JDK1.5之后出现的新特性
*
* 实现步骤:
* 1.在成员位置创建一个ReentrantLock对象
* 2.在访问了共享数据的代码前,调用lock方法,获取锁对象
* 3.在访问了共享数据的代码后,调用unlock方法,释放锁对象
*
*/
public class RunnableImpl implements Runnable{ //定义一个共享的票源
private int ticket = 100;
//1.在成员位置创建一个ReentrantLock对象
Lock l = new ReentrantLock(); @Override
public void run() {
//让卖票重复执行
while(true){
//2.在访问了共享数据的代码前,调用lock方法,获取锁对象
l.lock();
try {
//可能会出现安全问题的代码
//判断是否还有票
if(ticket>0){
//提高安全问题出现的概率,增加一个sleep
Thread.sleep(10);
//进行卖票
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
} } catch (Exception e) {
//异常的处理逻辑
System.out.println(e);
} finally {
//一定会执行的代码,资源释放
//3.在访问了共享数据的代码后,调用unlock方法,释放锁对象
l.unlock();//无论是否异常,都会释放掉锁对象
}
} }
}
多线程 lock 锁 示例
多线程线程图
线程的五种状态:
新建状态-->运行状态-->死亡(结束)状态
阻塞状态
冻结状态(休眠/无限等待)
新建 :刚创建出来的线程,即 new Thread();
阻塞 :没有抢到CPU,在等待CPU调用
运行 :调用run 方法,在运行状态
死亡 :方法结束,调用完成
休眠 :调用sleep() 方法,进入休眠状态
sleep 是Thread 的一个函数
sleep 指占用CPU 不工作,其他线程无法进入。即:sleep不会让出系统资源;
无限等待 : 调用wait() 方法,未被唤醒
wait 是object 的一个函数,需要调用notify() 来唤醒
wait 指不占用CPU 不工作,其他线程可以进入。即:wait是进入线程等待池中等待,让出系统资源。

java中的多线程 // 基础的更多相关文章
- Java中的多线程基础
1.线程与进程 进程: 进程是程序运行以及资源分配的基本单位,一个程序至少有一个进程. 如下图所示: 线程: 线程是CPU调度和分配的基本单位,一个进程至少有一个线程. 同一个进程中的线程共享进程资源 ...
- Java中的多线程=你只要看这一篇就够了
如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个话其 ...
- Java中的多线程技术全面详解
本文主要从整体上介绍Java中的多线程技术,对于一些重要的基础概念会进行相对详细的介绍,若有叙述不清晰或是不正确的地方,希望大家指出,谢谢大家:) 为什么使用多线程 并发与并行 我们知道,在单核机器上 ...
- Java 中传统多线程
目录 Java 中传统多线程 线程初识 线程的概念 实现线程 线程的生命周期 常用API 线程同步 多线程共享数据的问题 线程同步及实现机制 线程间通讯 线程间通讯模型 线程中通讯的实现 @(目录) ...
- 第87节:Java中的Bootstrap基础与SQL入门
第87节:Java中的Bootstrap基础与SQL入门 前言复习 什么是JQ? : write less do more 写更少的代码,做更多的事 找出所有兄弟: $("div" ...
- Java中使用多线程、curl及代理IP模拟post提交和get访问
Java中使用多线程.curl及代理IP模拟post提交和get访问 菜鸟,多线程好玩就写着玩,大神可以路过指教,小弟在这受教,谢谢! 更多分享请关注微信公众号:lvxing1788 ~~~~~~ 分 ...
- 【转】Java中的多线程学习大总结
多线程作为Java中很重要的一个知识点,在此还是有必要总结一下的. 一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程 ...
- Java中的 多线程编程
Java 中的多线程编程 一.多线程的优缺点 多线程的优点: 1)资源利用率更好2)程序设计在某些情况下更简单3)程序响应更快 多线程的代价: 1)设计更复杂虽然有一些多线程应用程序比单线程的应用程序 ...
- Android学习记录(5)—在java中学习多线程下载之断点续传②
在上一节中我们学习了在java中学习多线程下载的基本原理和基本用法,我们并没有讲多线程的断点续传,那么这一节我们就接着上一节来讲断点续传,断点续传的重要性不言而喻,可以不用重复下载,也可以节省时间,实 ...
随机推荐
- linux 在命令行中通过conda使用anaconda
在 ~/.bash_profile中添加 export PATH="/home/taoke/anaconda/bin:$PATH"
- oracle命令生成AWR报告
--命令生成AWR报告oracle@linux:~> sqlplus / as sysdba SQL*Plus: Release 11.1.0.7.0 - Production on Fri A ...
- 头像截图上传三种方式之一(一个简单易用的flash插件)(asp.net版本)
flash中有版权声明,不适合商业开发.这是官网地址:http://www.hdfu.net/ 本文参考了http://blog.csdn.net/yafei450225664/article/det ...
- kvm安装准备
到实际情况下,做虚拟化是直接做在真机上. 但实验时,可以在虚拟机上进行.(因为做实验的时候没办法连接到桥接模式的网络,所以使用了NAT方式来连接网络) 在vmware安装centos 64bit fo ...
- Suse Linux下NTP缓慢调整配置,转载至http://www.gpstime.com.cn/
(1)系统内若有使用crontab 进行与时间调整相关的例行性工作排程,应注释掉(命令人工crontab -e修改,删除定时同步任务ntpdate -s ntpserver). (2)修改ntp配置文 ...
- Percona XtraDB Cluster(PXC)-高可用架构设计说明
Mycat+PXC高可用集群 一.架构图 架构说明: 1.mysql 集群高可用部分: l 针对业务场景选用Percona XtraDB Cluter(PXC)复制集群.两个片集群 PXC-dataN ...
- python【项目】:工资管理(简易版)
功能要求: 登录系统用户认证通过后才能列出下一级菜单员工信息表 登录系统要有用户登录.注册账号.删除账号.修改密码.退出 登录密码要有加密功能 从info.txt文件读取员工及工资信息,最后通过增加, ...
- webStorage,离线缓存
一.webStorage 1.目标 1.了解cookie的不足之处,引入webstorage的概念 2.学习并且掌握webstorage有哪两种 3.学习并且掌握sessionStorag ...
- JavaWeb知识回顾-Servlet常用类、接口及其方法
今天主要把servlet的一些常用的类.接口和方法简单回顾一下. javax.servlet包 1.javax.servlet.Servlet接口 接口用于开发servlet,所有的servlet都要 ...
- 关于wordpress插件WP SMTP的邮箱设置
花了两天的时间把邮箱设置好了,把大概的步骤写下,放一下查到的资料. 1.去域名服务商那里添加MX记录 如下图的MX 2.测试主机是否禁用了mail()函数 参考链接wo ...