java基础多线程
线程的创建
方式1:继承Java.lang.Thread类,并覆盖run() 方法
package com.demo.Thread; public class ThreadDemo01 extends Thread{
public static void main(String[] args) {
new ThreadDemo01().start();
}
@Override
public void run() {
int count = 50;
while(count>0) {
System.out.println(count--);
}
}
}
继承Thread
方式2:实现Java.lang.Runnable接口,并实现run() 方法
package com.demo.Thread; public class ThreadDemo01 {
public static void main(String[] args) {
Thread th = new Thread(new Runnable() {
public void run() {
int count = 50;
while(count>0) {
System.out.println(count--);
}
}
}); th.start();
}
}
实现run()接口
线程的生命周期
新生状态 (new)
- 用new关键字建立一个线程对象后,该线程对象就处于新生状态。
- 处于新生状态的线程有自己的内存空间,通过调用start进入就绪状态
就绪状态(Runnable)
- 处于就绪状态线程具备了运行条件,但还没分配到CPU,处于线程就绪队列,等待系统为其分配CPU
- 当系统选定一个等待执行的线程后,它就会从就绪状态进入执行状态,该动作称之为“cpu调度”。
运行状态(Running)
- 在运行状态的线程执行自己的run方法中代码,直到等待某资源而阻塞或完成任务而死亡。
- 如果在给定的时间片内没有执行结束,就会被系统给换下来回到等待执行状态。
阻塞状态(Blocked)
阻塞状态是线程因为某种原因放弃CPU使用权限,暂时停止运行。直到线程进入就绪状态,才有机会进入运行状态。阻塞的三种情况
- 等待阻塞:通过调用线程的wait()方法,让线程等待某工作的完成。
- 同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),它会进入同步阻塞状态。
- 其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态:
死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有三个
- 正常运行的线程完成了它的全部工作;
- 线程被强制性地终止,如通过执行stop方法来终止一个线程--容易导致死锁,不推荐。
- 线程抛出未捕获的异常
Thread类常用方法
String |
getName() 返回该线程的名称。 |
static void |
sleep(long millis) 在指定的毫秒数内让线程休眠 |
void |
start() 使该线程开始执行 |
Void |
join() 等待该线程终止。 |
static void |
yield() 暂停当前正在执行的线程对象,并执行其他线程 |
static Thread |
currentThread() 返回对当前正在执行的线程对象 |
sleep()方法
sleep()使当前线程进入停滞状态(阻塞当前线程),让出CUP的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会;
sleep()是Thread类的Static(静态)的方法;因此他不能改变对象的机锁,所以当在一个Synchronized块中调用Sleep()方法是,线程虽然休眠了,但是对象的机锁并木有被释放,其他线程无法访问这个对象(即使睡着也持有对象锁)。
在sleep()休眠时间期满后,该线程不一定会立即执行,这是因为其它线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。
wait()方法
wait()方法是Object类里的方法;当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去(释放)了对象的机锁(暂时失去机锁,wait(long timeout)超时时间到后还需要返还对象锁);其他线程可以访问;
wait()使用notify或者notifyAlll或者指定睡眠时间来唤醒当前等待池中的线程。
wiat()必须放在synchronized block中,否则会在program runtime时扔出”java.lang.IllegalMonitorStateException“异常。
package com.demo.reg; public class TestD { public static void main(String[] args) {
new Thread(new Thread1()).start();
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
new Thread(new Thread2()).start();
} private static class Thread1 implements Runnable{
@Override
public void run(){
synchronized (TestD.class) {
System.out.println("enter thread1...");
System.out.println("thread1 is waiting...");
try {
//调用wait()方法,线程会放弃对象锁,进入等待此对象的等待锁定池
//只有当它被notify()时候,且对象锁没有被持有就能够继续运行下去
TestD.class.wait();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("thread1 is going on ....");
System.out.println("thread1 is over!!!");
}
}
} private static class Thread2 implements Runnable{
@Override
public void run(){
synchronized (TestD.class) {
System.out.println("enter thread2....");
System.out.println("thread2 is sleep....");
//只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
TestD.class.notify();
try {
//在调用sleep()方法的过程中,线程不会释放对象锁,
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("thread2 is going on....");
System.out.println("thread2 is over!!!");
}
}
}
// 控制台结果
// enter thread1...
// thread1 is waiting...
// enter thread2....
// thread2 is sleep....
// thread2 is going on....
// thread2 is over!!!
// thread1 is going on ....
// thread1 is over!!! }
线程礼让Yield
static void yield ()
• 让当前正在执行线程暂停,不是阻塞线程,而是将线程转入就绪状态
• 如果调用了yield方法之后,没有其他等待执行的线程,这个时候当前线程就会马上恢复执行!
package com.demo.Thread; public class ThreadDemo01 {
public static void main(String[] args) {
new Thread(new YieldDemo()).start(); new Thread(new Runnable() {
@Override
public void run() {
for(int i = 1;i <= 50;i++) {
System.out.println("Thread:"+i);
}
}
}).start(); }
} class YieldDemo implements Runnable{
public void run() {
for(int i = 1;i <= 50;i++) {
System.out.println("YieldDemo:"+i);
if(i%5==0) {
System.out.println("###########礼让###########");
Thread.yield();
} }
}
}
线程插队Join
void join()
合并线程,等待此线程执行完毕后,在执行其他线程,其他线程进入阻塞状态
为什么要用join()方法
在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了
观察线程状态变化
public Thread.State getState()
返回该线程的状态。 该方法用于监视系统状态,不用于同步控制。
枚举
Thread.State
线程状态。线程可以处于下列状态之一:
NEW
尚未启动的线程处于这种状态
RUNNABLE
可运行线程的线程状态,就绪状态和运行状态
BLOCKED
受阻塞并等待某个监视器锁的线程处于这种状态
WAITING
无限期地等待另一个线程来执行某一特定操作的线程处于这种状态,某一线程因为调用下列方法之一而处于等待状态。
- 有时间的wait
- 有时间的.join
TIMED_WAITING
等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态,某一线程因为调用以下带有指定正等待时间的方法之一而处于定时等待状态:
- sleep
- 有时间的 wait
- 有时间的join
TERMINATED
线程结束死亡
package com.demo.Thread; public class ThreadDemo01 {
public static void main(String[] args) { Thread t = new Thread(()-> {
// 每500毫秒阻塞一次
for(int i = 0;i < 5;i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
} }); // 新生状态
System.out.println("状态:"+t.getState()); // 启动线程
t.start(); // 每500毫秒查看线程的状态
for(int i = 0;i < 8;i++) {
try {
System.out.println("状态:"+t.getState());
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/*
控制台输出
状态:NEW
状态:RUNNABLE
状态:TIMED_WAITING
状态:TIMED_WAITING
状态:TIMED_WAITING
状态:TIMED_WAITING
状态:RUNNABLE
状态:TERMINATED
状态:TERMINATED
*/
}
}
线程优先级
Thread类 提供三种优先级常量
线程优先级1-10,
优先级越大代表被cpu优先执行的概率越大
package com.demo.Thread; public class ThreadDemo01 {
public static void main(String[] args) {
// 设置线程的名字
Thread old = new Thread(new MyThread(),"老人");
Thread child = new Thread(new MyThread(),"小孩");
Thread woman = new Thread(new MyThread(),"女人");
Thread man = new Thread(new MyThread(),"男人");
// 设置优先级(必须在启动线程之前设置优先级)
old.setPriority(Thread.MAX_PRIORITY);
child.setPriority(Thread.MAX_PRIORITY);
woman.setPriority(Thread.MIN_PRIORITY);
man.setPriority(Thread.MIN_PRIORITY);
// 启动线程
man.start();
woman.start();
child.start();
old.start(); // old.start();
// child.start();
// woman.start();
// man.start();
}
} class MyThread implements Runnable{
public void run() {
// 输出当前线程的名字和优先级
System.out.println(Thread.currentThread().getName()+"------>"+Thread.currentThread().getPriority());
}
}
测试线程优先级
守护线程
守护线程是给其他程提供服务用的,比如gc就是守护线程。
那么如果所有的非守护线程都执行完毕后,那么守护线程和JVM就会一起结束(因为没有可以守护的线程,当然守护线程就结束)
主线程结束后,普通线程继续执行。
package com.demo.Thread; public class ThreadDemo01 {
public static void main(String[] args) {
Thread t = new Thread(()-> {
// 无限循环
while(true) {
try {
System.out.println("我是普通线程");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
} }
}); t.start(); for(int i = 1;i <= 5;i++) {
try {
System.out.println("main线程 time="+i);
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
} // 控制台结果
// main线程 time=1
// 我是普通线程
// main线程 time=2
// 我是普通线程
// 我是普通线程
// main线程 time=3
// main线程 time=4
// 我是普通线程
// 我是普通线程
// main线程 time=5
// 我是普通线程
// 我是普通线程
// 我是普通线程
// 我是普通线程
// ...
// ...
// ...
// ... 一直输出下去
}
}
普通线程
设置为守护线程的时候,非守护线程全部结束后,守护线程和jVM一并结束
package com.demo.Thread; public class ThreadDemo01 {
public static void main(String[] args) {
Thread t = new Thread(()-> {
// 无限循环
while(true) {
try {
System.out.println("我是普通线程");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
} }
});
// 设置为守护线程,如果非主线程全部结束后,守护线程将会结束
t.setDaemon(true);
t.start(); for(int i = 1;i <= 5;i++) {
try {
System.out.println("main线程 time="+i);
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
} // 控制台打印
// main线程 time=1
// 我是普通线程
// 我是普通线程
// main线程 time=2
// 我是普通线程
// main线程 time=3
// 我是普通线程
// main线程 time=4
// 我是普通线程
// main线程 time=5
// 我是普通线程 }
}
守护线程
synchronized
一
synchronized关键字的作用域有二种:
- 是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
- 是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。
二
除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/*区块*/},它的作用域是当前对象;
三
synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法;
A.无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。
B.每个对象只有一个锁(lock)与之相关联。
任务调度
Timer
一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
构造方法
Timer(String name, boolean isDaemon) 创建一个新计时器,其相关的线程具有指定的名称,并且可以指定作为守护程序运行 |
方法
void schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务 |
void schedule(TimerTask task, Date firstTime, long period) 安排指定的任务在指定的时间开始进行重复的固定延迟执行 |
void schedule(TimerTask task, long delay) 安排在指定延迟后执行指定的任务 |
void schedule(TimerTask task, long delay, long period) 安排指定的任务从指定的延迟后开始进行重复的固定延迟执行 |
TimerTask
由 Timer 安排为一次执行或重复执行的任务。
方法
abstract void run() 此计时器任务要执行的操作 |
参考
https://www.cnblogs.com/GarfieldEr007/p/5746362.html
java基础多线程的更多相关文章
- Java基础-多线程-③线程同步之synchronized
使用线程同步解决多线程安全问题 上一篇 Java基础-多线程-②多线程的安全问题 中我们说到多线程可能引发的安全问题,原因在于多个线程共享了数据,且一个线程在操作(多为写操作)数据的过程中,另一个线程 ...
- Java基础-多线程-②多线程安全问题
什么是线程的安全问题? 上一篇 Java基础-多线程-①线程的创建和启动 我们说使用实现Runnable接口的方式来创建线程,可以实现多个线程共享资源: class Dog implements Ru ...
- java基础-多线程应用案例展示
java基础-多线程应用案例展示 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.两只熊,100只蜜蜂,蜜蜂每次生产的蜂蜜量是1,罐子的容量是30,熊在罐子的蜂蜜量达到20的时候 ...
- java基础-多线程二
java基础-多线程二 继承thread和实现Runnable的多线程每次都需要经历创建和销毁的过程,频繁的创建和销毁大大影响效率,线程池的诞生就可以很好的解决这一个问题,线程池可以充分的利用线程进行 ...
- 备战金三银四!一线互联网公司java岗面试题整理:Java基础+多线程+集合+JVM合集!
前言 回首来看2020年,真的是印象中过的最快的一年了,真的是时间过的飞快,还没反应过来年就夸完了,相信大家也已经开始上班了!俗话说新年新气象,马上就要到了一年之中最重要的金三银四,之前一直有粉丝要求 ...
- java基础多线程之共享数据
java基础巩固笔记5-多线程之共享数据 线程范围内共享数据 ThreadLocal类 多线程访问共享数据 几种方式 本文主要总结线程共享数据的相关知识,主要包括两方面:一是某个线程内如何共享数据,保 ...
- Java基础——多线程
Java中多线程的应用是非常多的,我们在Java中又该如何去创建线程呢? http://www.jianshu.com/p/40d4c7aebd66 一.常用的有三种方法来创建多线程 新建一个类继承自 ...
- Java基础--多线程的方方面面
1,什么是线程?线程和进程的区别是什么? 2,什么是多线程?为什么设计多线程? 3,Java种多线程的实现方式是什么?有什么区别? 4,线程的状态控制有哪些方法? 5,线程安全.死锁和生产者--消费者 ...
- AJPFX: Java基础多线程(一)
多线程是Java学习的非常重要的方面,是每个Java程序员必须掌握的基本技能.本文只是多线程细节.本质的总结,并无代码例子入门,不适合初学者理解.初学者学习多线程,建议一边看书.看博文,以便写代码尝试 ...
- 十三、Java基础---------多线程总结
多线程概述 理解多线程首先应明确线程,要了解线程就必须了解什么是进程. 1.进程 是一个正在执行的程序. 每一个进程执行都有一个执行顺序.该顺序是一个执行路径,或者叫一个控制单元. 2.线程 就是进程 ...
随机推荐
- PAT 1004 Counting Leaves (30分)
1004 Counting Leaves (30分) A family hierarchy is usually presented by a pedigree tree. Your job is t ...
- yaml服务部署示例
apiVersion: apps/v1kind: Deploymentmetadata: name: igirl namespace: chaolai labels: app: igirl ...
- 路由器安全-NetFlow
1.NetFlow介绍 提供高层次的诊断,分类和识别网络异常. 使用NetFlow来检查哪些行为改变明显的攻击是非常有效的. 就像Wiretap一样捕获数据包. NetFlow像电话账单.(谁和谁在通 ...
- vue-cli项目结构详解
vue-cli的webpack模板项目配置文件分析 https://blog.csdn.net/hongchh/article/details/55113751/ 由于最近在vue-cli生成的web ...
- java web 学生信息录入
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...
- iOS 开发之应用内弹出 App Store 应用界面
在APP内给其他APP做推广,经常用到在应用内弹出应用的APP #import <StoreKit/SKStoreProductViewController.h> 设置代理:<SKS ...
- JIRA 安装与破解 Mac版
1.安装 JDK 2.安装 MySQL 下载地址:https://dev.mysql.com/downloads/ 选择对应版本下载安装.我选择的是 mysql-8.0.11-macos10.13-x ...
- 为typecho添加分类描述
typecho 默认主题不显示分类描述,可以调整为显示 按找官方文档(点击查看),获取分类描述的代码为: <?php echo $this->getDescription(); ?> ...
- Style 继承
在应用某个主题后,想在此基础上自定义新的样式,可以使用如下方式继承样式. <Style x:Key="ListViewItemStyle" TargetType=" ...
- 「USACO5.5」矩形周长Picture
题目描述 墙上贴着许多形状相同的海报.照片.它们的边都是水平和垂直的.每个矩形图片可能部分或全部的覆盖了其他图片.所有矩形合并后的边长称为周长. 编写一个程序计算周长. 如图1所示7个矩形. 如图2所 ...