java线程day-02
1.什么是线程
* 线程是程序执行的一条路径, 一个进程中可以包含多条线程
* 多线程并发执行可以提高程序的效率, 可以同时完成多项工作
2.多线程的应用场景
* 红蜘蛛同时共享屏幕给多个电脑
* 迅雷开启多条线程一起下载
* QQ同时和多个人一起视频
* 服务器同时处理多个客户端请求
3.多线程并行和并发的区别
* 并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
* 并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。
* 比如我跟两个网友聊天,左手操作一个电脑跟甲聊,同时右手用另一台电脑跟乙聊天,这就叫并行。
* 如果用一台电脑我先给甲发个消息,然后立刻再给乙发消息,然后再跟甲聊,再跟乙聊。这就叫并发。
4.ava程序运行原理和JVM的启动是多线程的吗
* A:Java程序运行原理
* Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。
* B:JVM的启动是多线程的吗
* JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。
5. Thread类API的解析
public class Threadextends Objectimplements Runnable
线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。每个线程都可以或不可以标记为一个守护程序。当某个线程中运行的代码创建一个新 Thread
对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程才是守护程序。
当 Java 虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的 main
方法)。Java 虚拟机会继续执行线程,直到下列任一情况出现时为止:
- 调用了
Runtime
类的exit
方法,并且安全管理器允许退出操作发生。 - 非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,还是通过抛出一个传播到
run
方法之外的异常。
创建新执行线程有两种方法。一种方法是将类声明为 Thread
的子类。该子类应重写 Thread
类的 run
方法。
6.多线程程序实现的方式1
* 定义类继承Thread
* 重写run方法
* 把新线程要做的事写在run方法中
* 创建线程对象
* 开启新线程, 内部会自动执行run方法
示例程序:
public static void main(String[] args) {
MyThread mt = new MyThread(); //4,创建Thread类的子类对象
mt.start(); //5,开启线程 for(int i = 0; i < 1000; i++) {
System.out.println("bb");
}
} } class MyThread extends Thread { //1,继承Thread
public void run() { //2,重写run方法
for(int i = 0; i < 1000; i++) { //3,将要执行的代码写在run方法中
System.out.println("aaaaaaaaaaaa");
}
}
7.Runnable接口API的解析
public interface Runnable
Runnable
接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run
的无参数方法。
设计该接口的目的是为希望在活动时执行代码的对象提供一个公共协议。例如,Thread
类实现了 Runnable
。激活的意思是说某个线程已启动并且尚未停止。
此外,Runnable
为非 Thread
子类的类提供了一种激活方式。通过实例化某个 Thread
实例并将自身作为运行目标,就可以运行实现 Runnable
的类而无需创建 Thread
的子类。大多数情况下,如果只想重写 run()
方法,而不重写其他 Thread
方法,那么应使用 Runnable
接口。这很重要,因为除非程序员打算修改或增强类的基本行为,否则不应为该类创建子类。
8.多线程程序实现的方式2
*定义类实现Runnable接口
* 实现run方法
* 把新线程要做的事写在run方法中
* 创建自定义的Runnable的子类对象
* 创建Thread对象, 传入Runnable
* 调用start()开启新线程, 内部会自动调用Runnable的run()方法
public static void main(String[] args) {
MyRunnable mr = new MyRunnable(); //4,创建Runnable的子类对象
//Runnable target = mr; mr = 0x0011
Thread t = new Thread(mr); //5,将其当作参数传递给Thread的构造函数
t.start(); //6,开启线程 for(int i = 0; i < 1000; i++) {
System.out.println("bb");
}
} } class MyRunnable implements Runnable { //1,定义一个类实现Runnable @Override
public void run() { //2,重写run方法
for(int i = 0; i < 1000; i++) { //3,将要执行的代码写在run方法中
System.out.println("aaaaaaaaaaaa");
}
}
为什么mr.start调用run方法 ,因为target一层层传,最后调用的就是,thread中的run方法。
mr相当于记录new MyRunnable的地址值,编译看的是父类,运行看的是子类,target.run相当于自己写的实现类的run
private Runnable target;
public void run() {
if (target != null) {
target.run();
}
}
9.两种方式的区别
查看源码的区别:
* a.继承Thread : 由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法
* b.实现Runnable : 构造函数中传入了Runnable的引用, 成员变量记住了它, start()调用run()方法时内部判断成员变量Runnable的引用是否为空, 不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法
* 继承Thread
* 好处是:可以直接使用Thread类中的方法,代码简单
* 弊端是:如果已经有了父类,就不能用这种方法
* 实现Runnable接口
* 好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的
* 弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂
10.匿名内部类实现线程的两种方式
* 继承Thread类
new Thread() { //1,new 类(){}继承这个类
public void run() { //2,重写run方法
for(int i = 0; i < 3000; i++) { //3,将要执行的代码,写在run方法中
System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");
}
}
}.start();
* 实现Runnable接口
new Thread(new Runnable() { //1,将Runnable的子类对象传递给Thread的构造方法
public void run() { //2,重写run方法
for(int i = 0; i < 1000; i++) { //3,将要执行的代码写在run方法中
System.out.println("bb");
}
}
}).start(); //4,开启线程
11.获取名字和设置名字
通过构造设置名字
public static void demo1() {
new Thread("芙蓉姐姐") { //通过构造方法给name赋值
public void run() {
System.out.println(this.getName() + "....aaaaaaaaa");
}
}.start(); new Thread("凤姐") {
public void run() {
System.out.println(this.getName() + "....bb");
}
}.start();
}
通过setName方式
Thread t1 = new Thread() { //父类引用指向子类对象,new Thread(){}匿名内部类,相当于子类对象
public void run() {
//this.setName("张三");
System.out.println(this.getName() + "....aaaaaaaaaaaaa");
}
}; Thread t2 = new Thread() {
public void run() {
//this.setName("李四");
System.out.println(this.getName() + "....bb");
}
}; t1.setName("张三");
t2.setName("李四");
t1.start();
t2.start();
}
12.获取当前线程的对象
public static void main(String[] args) {
new Thread() {
public void run() {
System.out.println(getName() + "....aaaaaa");
}
}.start(); new Thread(new Runnable() {
public void run() {
//Thread.currentThread()获取当前正在执行的线程
System.out.println(Thread.currentThread().getName() + "...bb");
}
}).start(); Thread.currentThread().setName("我是主线程");
System.out.println(Thread.currentThread().getName());
}
13.休眠线程
1.主线程中让线程休眠
public static void demo1() throws InterruptedException {
for(int i = 20; i >= 0; i--) {
Thread.sleep(1000);
System.out.println("倒计时第" +i + "秒");
}
}
2.子类中休眠
public static void main(String[] args) throws InterruptedException {
//demo1();
new Thread() {
public void run() {
for(int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) { e.printStackTrace();
}
System.out.println(getName() + "...aaaaaaaaaa");
}
}
}.start(); new Thread() {
public void run() {
for(int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) { e.printStackTrace();
}
System.out.println(getName() + "...bb");
}
}
}.start();
}
14.守护线程
setDaemon(), 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出
public static void main(String[] args) {
Thread t1 = new Thread() {
public void run() {
for(int i = 0; i < 2; i++) {
System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaa");
}
}
}; Thread t2 = new Thread() {
public void run() {
for(int i = 0; i < 50; i++) {
System.out.println(getName() + "...bb");
}
}
}; t2.setDaemon(true); //设置为守护线程 t1.start();
t2.start();
}
15.加入线程
join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
join(int), 可以等待指定的毫秒之后继续
public static void main(String[] args) {
final Thread t1 = new Thread() { //匿名内部类在使用它所在方法中的局部变量的时候,局部变量必须用final修饰
public void run() {
for(int i = 0; i < 10; i++) {
System.out.println(getName() + "...aaaaaaaaaaaaa");
}
}
}; Thread t2 = new Thread() {
public void run() {
for(int i = 0; i < 10; i++) {
if(i == 2) {
try {
//t1.join();
t1.join(1); //插队指定的时间,过了指定时间后,两条线程交替执行
} catch (InterruptedException e) { e.printStackTrace();
}
}
System.out.println(getName() + "...bb");
}
}
}; t1.start();
t2.start();
}
16.礼让线程
/**
* yield让出cpu礼让线程
*/
public static void main(String[] args) {
new MyThread().start();
new MyThread().start();
} } class MyThread extends Thread {
public void run() {
for(int i = 1; i <= 1000; i++) {
if(i % 10 == 0) {
Thread.yield(); //让出CPU
}
System.out.println(getName() + "..." + i);
}
}
17.设置线程的优先级
setPriority()设置线程的优先级
源码,线程最大的优先级是10
public final static int MAX_PRIORITY = 10;
默认的优先级是:5
public final static int NORM_PRIORITY = 5;
最小是:1
public final static int MIN_PRIORITY = 1;
设置时,不能超过1到10的范围
public static void main(String[] args) {
Thread t1 = new Thread(){
public void run() {
for(int i = 0; i < 100; i++) {
System.out.println(getName() + "...aaaaaaaaa" );
}
}
}; Thread t2 = new Thread(){
public void run() {
for(int i = 0; i < 100; i++) {
System.out.println(getName() + "...bb" );
}
}
}; //t1.setPriority(10); 设置最大优先级
//t2.setPriority(1); t1.setPriority(Thread.MIN_PRIORITY); //设置最小的线程优先级
t2.setPriority(Thread.MAX_PRIORITY); //设置最大的线程优先级 t1.start();
t2.start();
}
18.同步代码块
使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
多个同步代码块如果使用相同的锁对象, 那么他们就是同步的
同步代码块
* 使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
* 多个同步代码块如果使用相同的锁对象, 那么他们就是同步的
public static void main(String[] args) {
final Printer p = new Printer(); new Thread() {
public void run() {
while(true) {
p.print1();
}
}
}.start(); new Thread() {
public void run() {
while(true) {
p.print2();
}
}
}.start();
} } class Printer {
Demo d = new Demo();
public void print1() {
//synchronized(new Demo()) { //同步代码块,锁机制,锁对象可以是任意的
synchronized(d) {
System.out.print("黑");
System.out.print("马");
System.out.print("程");
System.out.print("序");
System.out.print("员");
System.out.print("\r\n");
}
} public void print2() {
//synchronized(new Demo()) { //锁对象不能用匿名对象,因为匿名对象不是同一个对象
synchronized(d) {
System.out.print("传");
System.out.print("智");
System.out.print("播");
System.out.print("客");
System.out.print("\r\n");
}
}
} class Demo{}
19.同步方法
使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的
public static void main(String[] args) {
final Printer2 p = new Printer2(); new Thread() {
public void run() {
while(true) {
p.print1();
}
}
}.start(); new Thread() {
public void run() {
while(true) {
p.print2();
}
}
}.start();
} } class Printer2 {
Demo d = new Demo();
//非静态的同步方法的锁对象是神马?
//答:非静态的同步方法的锁对象是this
//静态的同步方法的锁对象是什么?
//是该类的字节码对象
public static synchronized void print1() { //同步方法只需要在方法上加synchronized关键字即可
System.out.print("黑");
System.out.print("马");
System.out.print("程");
System.out.print("序");
System.out.print("员");
System.out.print("\r\n");
} public static void print2() {
//synchronized(new Demo()) { //锁对象不能用匿名对象,因为匿名对象不是同一个对象
synchronized(Printer2.class) { //也可以在类中声明一个静态的对象,让print()和print2()传入相同的对象,此实例为了和print1()保持一致使用类的字节码对象
System.out.print("传");
System.out.print("智");
System.out.print("播");
System.out.print("客");
System.out.print("\r\n");
}
}
20.线程安全问题
* 多线程并发操作同一数据时, 就有可能出现线程安全问题
* 使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作
模拟售票
/**
* 需求:铁路售票,一共100张,通过四个窗口卖完.
*/
public static void main(String[] args) {
new Ticket().start();
new Ticket().start();
new Ticket().start();
new Ticket().start();
} } class Ticket extends Thread {
private static int ticket = 100;
//private static Object obj = new Object(); //如果用引用数据类型成员变量当作锁对象,必须是静态的
public void run() {
while(true) {
synchronized(Ticket.class) {
if(ticket <= 0) {
break;
}
try {
Thread.sleep(10); //线程1睡,线程2睡,线程3睡,线程4睡
} catch (InterruptedException e) { e.printStackTrace();
}
System.out.println(getName() + "...这是第" + ticket-- + "号票");
}
}
}
21.火车站卖票的例子用实现Runnable接口
/**
*
* 火车站卖票的例子用实现Runnable接口
*/
public static void main(String[] args) {
MyTicket mt = new MyTicket();
new Thread(mt).start();
new Thread(mt).start();
new Thread(mt).start();
new Thread(mt).start(); /*Thread t1 = new Thread(mt); //多次启动一个线程是非法的
t1.start();
t1.start();
t1.start();
t1.start();*/
} } class MyTicket implements Runnable {
private int tickets = 100;
@Override
public void run() {
while(true) {
synchronized(this) {
if(tickets <= 0) {
break;
}
try {
Thread.sleep(10); //线程1睡,线程2睡,线程3睡,线程4睡
} catch (InterruptedException e) { e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "...这是第" + tickets-- + "号票");
}
}
}
22.死锁
多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁
尽量不要嵌套使用
死锁Demo
private static String s1 = "筷子左";
private static String s2 = "筷子右"; public static void main(String[] args) {
new Thread() {
public void run() {
while(true) {
synchronized(s1) {
System.out.println(getName() + "...获取" + s1 + "等待" + s2);
synchronized(s2) {
System.out.println(getName() + "...拿到" + s2 + "开吃");
}
}
}
}
}.start(); new Thread() {
public void run() {
while(true) {
synchronized(s2) {
System.out.println(getName() + "...获取" + s2 + "等待" + s1);
synchronized(s1) {
System.out.println(getName() + "...拿到" + s1 + "开吃");
}
}
}
}
}.start();
}
23.线程安全的类回顾
看源码:Vector,StringBuffer,Hashtable,Collections.synchroinzed(xxx)
* Vector是线程安全的,ArrayList是线程不安全的
* StringBuffer是线程安全的,StringBuilder是线程不安全的
* Hashtable是线程安全的,HashMap是线程不安全的
Vector的add方法:
public synchronized void addElement(E obj) {
ArrayList的add方法:
public boolean add(E e) {
StringBuffert的append方法:
public synchronized StringBuffer append(Object obj) {
super.append(String.valueOf(obj));
return this;
}
StringBuilder的append方法:
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
Hashtable的put方法:
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
HashMap的put方法:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
Collections.synchroinzed(xxx)可以将其变成线程安全的,所以需要ArrayList安全的时候可以调用,所以可以淘汰Vector
java线程day-02的更多相关文章
- Java线程:概念与原理
Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...
- Java线程:创建与启动
Java线程:创建与启动 一.定义线程 1.扩展java.lang.Thread类. 此类中有个run()方法,应该注意其用法: public void run() 如果该线程是使用独立的 R ...
- java线程详解
Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...
- 怎样分析java线程堆栈日志
注: 该文章的原文是由 Tae Jin Gu 编写,原文地址为 How to Analyze Java Thread Dumps 当有障碍,或者是一个基于 JAVA 的 WEB 应用运行的比预期慢的时 ...
- Java 集合系列 02 Collection架构
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- Java线程详解----借鉴
Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...
- 如何创建并运行java线程
本文转载地址: http://ifeve.com/creating-and-starting-java-threads/ Java线程类也是一个object类,它的实例都继承自j ...
- java 线程(1)
Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...
- Java线程状态转换
前言:对于Java线程状态方面的知识点,笔者总感觉朦朦胧胧,趁着最近整理资料,将Java线程状态方面的知识点总结归纳,以便加深记忆. 1.Java线程状态值 在Thread类源码中通过枚举为线程定义了 ...
- 定位现网环境中最耗费CPU的Java线程
参考:JVM性能调优监控工具jps.jstack.jmap.jhat.jstat.hprof使用详解 下面通过一个实例找出某个Java进程中最耗费CPU的Java线程并定位堆栈信息,用到的命令有ps. ...
随机推荐
- spring-data-jdbc的基础使用(一)
前言 很多人知道Mybatis,知道Jpa,但是对spring-data-jdbc可能了解的少之又少.注意我们这里说的是data-jdbc,而不是普通的jdbc.它拥有了类似jpa的一些特性,比如能够 ...
- Typora+Markdown便捷发布blog
参考文章:https://www.cnblogs.com/Heroge/p/12459762.html 需要下载Typora和dotnet Typora下载链接:https://www.typora. ...
- SQL 练习32
查询不及格的课程 SELECT * from Course WHERE CId IN (SELECT cid from sc WHERE score < 60 GROUP BY cid)
- 什么?都1202年了还不懂k8s和容器的关系?!这份k8s指南快拿走不谢!
都1202年了,还是有许多人搞不清容器与k8s之间的关系.在开始本篇正文之前,我们先来捋一捋这对"CP"的关系. 你可能已经很熟悉虚拟机了,最常见的莫过于我们拿到macOS却需要用 ...
- 6、二进制安装K8s之部署kubectl
二进制安装K8s之部署kubectl 我们把k8s-master 也设置成node,所以先master上面部署node,在其他机器上部署node也适用,更换名称即可. 1.在所有worker node ...
- C# 通过反射获取特性的值
特性(Attribute)是用于在运行时传递程序中各种元素(比如类.方法.结构.枚举.组件等)的行为信息的声明性标签.您可以通过使用特性向程序添加声明性信息.一个声明性标签是通过放置在它所应用的元素前 ...
- java Math.random()生成从n到m的随机整数
Java中Math类的random()方法可以生成[0,1)之间的随机浮点数.而double类型数据强制转换成int类型,整数部分赋值给int类型变量,小数点之后的小数部分将会丢失. 如果要生成[0, ...
- Qt简单的解析Json数据例子(一)
要解析的json的格式为: { "rootpath": "001", "usernum": 111, "childdep" ...
- 13.SpringMVC之全局异常
我们知道,系统中异常包括:编译时异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发.测试通过手段减少运行时异常的发生.在开发中,不管是dao层 ...
- Spring详解(四)------注解配置DI
第一步:在 applicationContext.xml 中引入命名空间 这里我们简单讲解一下这里引入的命名空间,简单来说就是用来约束xml文件格式的.第一个 xmlns:context ,这表示标签 ...