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
void
start() 使该线程开始执行;Java 虚拟机调用该线程的
run
方法。static
Thread
currentThread() 返回对当前正在执行的线程对象的引用string getName() 返回该线程的名称
long getId() 返回该线程的标识符
int getPriority() 返回线程的优先级
Thread.State getState() 返回该线程的状态
void interrupt() 中断线程
void setName(String name) 改变线程名称,使之与参数
name
相同void setPriority(int newPriority)更改线程的优先级
static void
sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)static void
yield() 暂停当前正在执行的线程对象,并执行其他线程
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中学习多线程下载的基本原理和基本用法,我们并没有讲多线程的断点续传,那么这一节我们就接着上一节来讲断点续传,断点续传的重要性不言而喻,可以不用重复下载,也可以节省时间,实 ...
随机推荐
- react input 设置默认值
1.text类型 <input type="text" value={默认值} /> ,这种写法可以显示默认值,但不能对输入框进行编辑 正确写法: <input ...
- Android的休眠与唤醒
Android 休眠(suspend),在一个打过android补丁的内核中,state_store()函数会走另外一条路,会进入到request_suspend_state()中,这个文件在earl ...
- 64_m3
molequeue-doc-0.8.0-2.20161222giteb397e.fc26.no..> 05-Apr-2017 10:04 451570 molequeue-libs-0.8.0- ...
- 分布式系统的负载均衡以及ngnix负载均衡的五种策略
一般而言,有以下几种常见的负载均衡策略: 一.轮询. 特点:给每个请求标记一个序号,然后将请求依次派发到服务器节点中,适用于集群中各个节点提供服务能力等同且无状态的场景. 缺点:该策略将节点视为等同, ...
- ubuntu遇到的 the system is runing low-graphics mode 问题
不知道修改了什么,然后开机显示the system is runing low-graphics mode 看过博客使用如下方法成功进入系统,但是显示分辨率很低,显示 built-in display ...
- Ubuntu vi 上下左右变ABCD问题解决方法
---恢复内容开始--- 错误问题:vi上下左右键显示为ABCD的问题 解决方法: 只要依次执行以下两个命令即可完美解决Ubuntu下vi编辑器方向键变字母的问题. 一.执行命令 sudo apt-g ...
- day41 - 异步IO、协程
目录 (见右侧目录栏导航) - 1. 前言- 2. IO的五种模型- 3. 协程 - 3.1 协程的概念- 4. Gevent 模块 - 4.1 gevent 基本使用 - 4.2 ...
- leetcode 之Sum系列(七)
第一题是Two Sum 同样是用哈希表来做,需要注意的是在查打gap是要排除本身.比如target为4,有一个值为2,gap同样为2. vector<int> twoSum(vector& ...
- xss 过滤
一. xss过滤 用户通过Form获取展示在终端, 提交数据,Form验证里面加入xss验证(对用户提交的内容验证是否有关键标签) from django.conf.urls import url f ...
- centos7 lvs+keepalived nat模式
1.架构图 3.地址规划 主机名 内网ip 外网ip lvs-master 192.168.137.111(仅主机)eth1 172.16.76.111(桥接)eth0 lvs-slave 192 ...