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中学习多线程下载的基本原理和基本用法,我们并没有讲多线程的断点续传,那么这一节我们就接着上一节来讲断点续传,断点续传的重要性不言而喻,可以不用重复下载,也可以节省时间,实 ...
随机推荐
- 【leetcode 简单】第三十六题 最小栈
设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈. push(x) -- 将元素 x 推入栈中. pop() -- 删除栈顶的元素. top() -- 获取栈顶元素. ...
- 【leetcode 简单】第十题 实现strStr()
实现 strStr() 函数. 给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始).如果不存在,则返 ...
- 天梯赛 L2-009 抢红包
题目链接 没有人没抢过红包吧-- 这里给出N个人之间互相发红包.抢红包的记录,请你统计一下他们抢红包的收获. 输入格式: 输入第一行给出一个正整数N(<= 104),即参与发红包和抢红包的总人数 ...
- 全文搜索引擎 Elasticsearch 介绍
全文搜索属于最常见的需求,开源的 Elasticsearch (以下简称 Elastic)是目前全文搜索引擎的首选. 它可以快速地储存.搜索和分析海量数据.维基百科.Stack Overflow.Gi ...
- ORA-01552 非系统表空间不能使用系统回滚段处理
今天新搭建了一个10g的测试数据库,运行都很正常,但是在打开autotrace功能后执行语句,报错 SQL> set autotrace on SQL> select username,s ...
- frameset测试
frame不能放在body标签内.指定name属性,为这一个框架指定名字,在html的a的target属性可以设为target="right"在该框架显示跳转的页面.(常用于后台管 ...
- /proc/cpuinfo 文件分析(查看CPU信息)
/proc/cpuinfo文件分析 根据以下内容,我们则可以很方便的知道当前系统关于CPU.CPU的核数.CPU是否启用超线程等信息. <1>查询系统具有多少个逻辑核:cat /proc/ ...
- mysql 配置数据库主从同步
参考:https://www.cnblogs.com/kevingrace/p/6256603.html http://www.51testing.com/html/00/130600-243651. ...
- Webcollector应用(二)
先吐槽一句哀家的人品,总在写好代码之后,网站默默的升级,没有一点点防备... 一.加代理 爬取一个网站的时候,爬了不到一半,IP被封了,整个内部局域网的所有电脑都不能访问网站了. public cla ...
- Oracle dblink的说明和简单使用
在跨数据库查询的时候时常会用到dblink,例如:两台不同的数据库服务器,从一台数据库服务器的一个用户读取另一台数据库服务器下面的某个schema的数据,这个时候,使用dblink能够很方便的实现.d ...