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

 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中的多线程 // 基础的更多相关文章

  1. Java中的多线程基础

    1.线程与进程 进程: 进程是程序运行以及资源分配的基本单位,一个程序至少有一个进程. 如下图所示: 线程: 线程是CPU调度和分配的基本单位,一个进程至少有一个线程. 同一个进程中的线程共享进程资源 ...

  2. Java中的多线程=你只要看这一篇就够了

    如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个话其 ...

  3. Java中的多线程技术全面详解

    本文主要从整体上介绍Java中的多线程技术,对于一些重要的基础概念会进行相对详细的介绍,若有叙述不清晰或是不正确的地方,希望大家指出,谢谢大家:) 为什么使用多线程 并发与并行 我们知道,在单核机器上 ...

  4. Java 中传统多线程

    目录 Java 中传统多线程 线程初识 线程的概念 实现线程 线程的生命周期 常用API 线程同步 多线程共享数据的问题 线程同步及实现机制 线程间通讯 线程间通讯模型 线程中通讯的实现 @(目录) ...

  5. 第87节:Java中的Bootstrap基础与SQL入门

    第87节:Java中的Bootstrap基础与SQL入门 前言复习 什么是JQ? : write less do more 写更少的代码,做更多的事 找出所有兄弟: $("div" ...

  6. Java中使用多线程、curl及代理IP模拟post提交和get访问

    Java中使用多线程.curl及代理IP模拟post提交和get访问 菜鸟,多线程好玩就写着玩,大神可以路过指教,小弟在这受教,谢谢! 更多分享请关注微信公众号:lvxing1788 ~~~~~~ 分 ...

  7. 【转】Java中的多线程学习大总结

    多线程作为Java中很重要的一个知识点,在此还是有必要总结一下的. 一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程 ...

  8. Java中的 多线程编程

    Java 中的多线程编程 一.多线程的优缺点 多线程的优点: 1)资源利用率更好2)程序设计在某些情况下更简单3)程序响应更快 多线程的代价: 1)设计更复杂虽然有一些多线程应用程序比单线程的应用程序 ...

  9. Android学习记录(5)—在java中学习多线程下载之断点续传②

    在上一节中我们学习了在java中学习多线程下载的基本原理和基本用法,我们并没有讲多线程的断点续传,那么这一节我们就接着上一节来讲断点续传,断点续传的重要性不言而喻,可以不用重复下载,也可以节省时间,实 ...

随机推荐

  1. react input 设置默认值

    1.text类型 <input type="text" value={默认值} />  ,这种写法可以显示默认值,但不能对输入框进行编辑 正确写法: <input ...

  2. Android的休眠与唤醒

    Android 休眠(suspend),在一个打过android补丁的内核中,state_store()函数会走另外一条路,会进入到request_suspend_state()中,这个文件在earl ...

  3. 64_m3

    molequeue-doc-0.8.0-2.20161222giteb397e.fc26.no..> 05-Apr-2017 10:04 451570 molequeue-libs-0.8.0- ...

  4. 分布式系统的负载均衡以及ngnix负载均衡的五种策略

    一般而言,有以下几种常见的负载均衡策略: 一.轮询. 特点:给每个请求标记一个序号,然后将请求依次派发到服务器节点中,适用于集群中各个节点提供服务能力等同且无状态的场景. 缺点:该策略将节点视为等同, ...

  5. ubuntu遇到的 the system is runing low-graphics mode 问题

    不知道修改了什么,然后开机显示the system is runing low-graphics mode 看过博客使用如下方法成功进入系统,但是显示分辨率很低,显示 built-in display ...

  6. Ubuntu vi 上下左右变ABCD问题解决方法

    ---恢复内容开始--- 错误问题:vi上下左右键显示为ABCD的问题 解决方法: 只要依次执行以下两个命令即可完美解决Ubuntu下vi编辑器方向键变字母的问题. 一.执行命令 sudo apt-g ...

  7. day41 - 异步IO、协程

    目录 (见右侧目录栏导航) - 1. 前言- 2. IO的五种模型- 3. 协程    - 3.1 协程的概念- 4. Gevent 模块    - 4.1 gevent 基本使用    - 4.2 ...

  8. leetcode 之Sum系列(七)

    第一题是Two Sum 同样是用哈希表来做,需要注意的是在查打gap是要排除本身.比如target为4,有一个值为2,gap同样为2. vector<int> twoSum(vector& ...

  9. xss 过滤

    一. xss过滤 用户通过Form获取展示在终端, 提交数据,Form验证里面加入xss验证(对用户提交的内容验证是否有关键标签) from django.conf.urls import url f ...

  10. centos7 lvs+keepalived nat模式

    1.架构图   3.地址规划 主机名 内网ip 外网ip lvs-master 192.168.137.111(仅主机)eth1 172.16.76.111(桥接)eth0 lvs-slave 192 ...