多线程的基本概念
线程指进程中的一个执行场景,也就是执行流程,那么进程和线程有什么区别呢?
- 每个进程是一个应用程序,都有独立的内存空间
- 同一个进程中的线程共享其进程中的内存和资源(共享的内存是堆内存和方法区内存,栈内存不共享,每个线程有自己的。)
什么是进程?
一个进程对应一个应用程序。例如:在 windows 操作系统启动 Word 就表示启动了一个
进程。在 java 的开发环境下启动 JVM,就表示启动了一个进程。现代的计算机都是支持多
进程的,在同一个操作系统中,可以同时启动多个进程。
多进程有什么作用?
单进程计算机只能做一件事情。
玩电脑,一边玩游戏(游戏进程)一边听音乐(音乐进程)。
对于单核计算机来讲,在同一个时间点上,游戏进程和音乐进程是同时在运行吗?不是。
因为计算机的 CPU 只能在某个时间点上做一件事。由于计算机将在“游戏进程”和“音乐
进程”之间频繁的切换执行,切换速度极高,人类感觉游戏和音乐在同时进行。
多进程的作用不是提高执行速度,而是提高 CPU 的使用率。
进程和进程之间的内存是独立的。
什么是线程?
线程是一个进程中的执行场景。一个进程可以启动多个线程。
多线程有什么作用?
多线程不是为了提高执行速度,而是提高应用程序的使用率。
线程和线程共享“堆内存和方法区内存”,栈内存是独立的,一个线程一个栈。
可以给现实世界中的人类一种错觉:感觉多个线程在同时并发执行。
java 程序的运行原理?
java 命令会启动 java 虚拟机,启动 JVM,等于启动了一个应用程序,表示启动了一个进程。该进程会自动启动一个“主线程”,然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。在此之前的所有程序都是单线程的。
线程生命周期
线程是一个进程中的执行场景,一个进程可以启动多个线程
新建:采用 new 语句创建完成
就绪:执行 start 后
运行:占用 CPU 时间
阻塞:执行了 wait 语句、执行了 sleep 语句和等待某个对象锁,等待输入的场合
终止:退出 run()方法
多线程不是为了提高执行速度,而是提高应用程序的使用率.
线程和线程共享”堆内存和方法区内存”.栈内存是独立的,一个线程一个栈.
可以给现实世界中的人类一种错觉 : 感觉多线程在同时并发执行.
很多人都对其中的一些概念不够明确,如同步、并发等等,让我们先建立一个数据字典,以免产生误会。
- 多线程:指的是这个程序(一个进程)运行时产生了不止一个线程
- 并行与并发:
-
- 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
-
- 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。
线程安全:经常用来描绘一段代码。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存,cpu是不是够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果,如不加事务的转账代码:
void transferMoney(User from, User to, float amount){
to.setMoney(to.getBalance() + amount);
from.setMoney(from.getBalance() - amount);
}
- 1
- 2
- 3
- 4
- 5
- 6
同步:Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如上面的代码简单加入@synchronized关键字。在保证结果准确的同时,提高性能,才是优秀的程序。线程安全的优先级高于性能。
Java命令会启动Java虚拟机,启动JVM,等于启动了一个应用程序,表示启动了一个进程,该进程会自动启动一个”主线程”,
然后主线程去调用某个类的main()方法,所以main()方法运行在主线程中.
线程的调度与控制
线程的调度模型分为: 分时调度模型和抢占式调度模型,Java使用抢占式调度模型
通常我们的计算机只有一个 CPU,CPU 在某一个时刻只能执行一条指令,线程只有得到 CPU时间片,也就是使用权,才可以执行指令。在单 CPU 的机器上线程不是并行运行的,只有在多个 CPU 上线程才可以并行运行。Java 虚拟机要负责线程的调度,取得 CPU 的使用权,目前有两种调度模型:分时调度模型和抢占式调度模型,Java 使用抢占式调度模型。分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。
- 分时调度模型: 所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
- 抢占式调度模型: 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些.
public class ThreadTest {
public static void main(String[] args) {
ThreadTest1();
ThreadTest2();
ThreadTest3();
ThreadTest4();
ThreadTest5();
}
/**
* 三个方法: 获取当前线程对象:Thread.currentThread(); 给线程起名: t1.setName("t1"); 获取线程的名字: t.getName();
*/
private static void ThreadTest1() {
Thread t = Thread.currentThread();// t保存的内存地址指向的线程为"主线程"
System.out.println(t.getId());
Thread t1 = new Thread(new Processor1());
// 给线程起名
t1.setName("t1");
t1.start();
Thread t2 = new Thread(new Processor1());
t2.setName("t2");
t2.start();
}
/**
* 线程优先级高的获取的CPU时间片相对多一些 优先级: 1-10 最低: 1 最高: 10 默认: 5
*/
private static void ThreadTest2() {
Thread t1 = new Processor2();
Thread t2 = new Processor2();
t1.setName("t1");
t2.setName("t2");
System.out.println(t1.getPriority());
System.out.println(t2.getPriority());
t1.setPriority(1);
t2.setPriority(10);
t1.start();
t2.start();
}
/**
* 1.Thread.sleep(毫秒); 2.sleep方法是一个静态方法 3.该方法的作用: 阻塞当前线程,腾出CPU,让给其它线程
*/
private static void ThreadTest3() {
Thread t = new Thread(new Processor3());
t.start();
for (int i = 0; i < 11; i++) {
System.out.println(Thread.currentThread().getName() + "========>" + i);
try {
t.sleep(5000);// 等同于Thread.sleep(5000);阻塞的还是当前线程,和t线程无关.
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 某线程正在休眠,如何打断它的休眠 以下方式依靠的是异常处理机制
*/
private static void ThreadTest4() {
try {
Thread t = new Thread(new Processor4());
t.start();
Thread.sleep(5000);// 睡5s
t.interrupt();// 打断Thread的睡眠
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 如何正确的更好的终止一个正在执行的线程 需求:线程启动5s之后终止.
*/
private static void ThreadTest5() {
Processor5 p = new Processor5();
Thread t = new Thread(p);
t.start();
// 5s之后终止
try {
Thread.sleep(5000);
p.isRun = false;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class Processor1 implements Runnable {
@Override
public void run() {
Thread t = Thread.currentThread();// t保存的内存地址指向的线程为"t1线程对象"
System.out.println(t.getName());
System.out.println(t.getId());
}
}
class Processor2 extends Thread {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + "----------->" + i);
}
}
}
class Processor3 implements Runnable {
/**
* Thread中的run方法不能抛出异常,所以重写runn方法之后,在run方法的声明位置上不能使用throws 所以run方法中的异常只能try...catch...
*/
@Override
public void run() {
for (int i = 0; i < 11; i++) {
System.out.println(Thread.currentThread().getName() + "========>" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Processor4 implements Runnable {
@Override
public void run() {
try {
Thread.sleep(1000000000);
System.out.println("能否执行这里");
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 11; i++) {
System.out.println(Thread.currentThread().getName() + "========>" + i);
}
}
}
class Processor5 implements Runnable {
boolean isRun = true;
@Override
public void run() {
for (int i = 0; i < 11; i++) {
if (isRun) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "========>" + i);
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
本文所有代码已经完整附上,如果想查看运行结果,可以把代码直接复制到开发工具运行查看
线程优先级
线 程 优 先 级 主 要 分 三 种 : MAX_PRIORITY( 最 高 级 );MIN_PRIORITY ( 最 低 级 )NORM_PRIORITY(标准)默认
//设置线程的优先级,线程启动后不能再次设置优先级
//必须在启动前设置优先级
//设置最高优先级
t1.setPriority(Thread.MAX_PRIORITY);
- 1
- 2
- 3
- 4
- 5
sleep
sleep 设置休眠的时间,单位毫秒,当一个线程遇到 sleep 的时候,就会睡眠,进入到阻塞状态,放弃 CPU,腾出 cpu 时间片,给其他线程用,所以在开发中通常我们会这样做,使其他的线程能够取得 CPU 时间片,当睡眠时间到达了,线程会进入可运行状态,得到 CPU 时间片继续执行,如果线程在睡眠状态被中断了,将会抛出 IterruptedException
public class ThreadTest05 {
public static void main(String[] args) {
Runnable r1 = new Processor();
Thread t1 = new Thread(r1, "t1");
t1.start();
Thread t2 = new Thread(r1, "t2");
t2.start();
}
}
class Processor implements Runnable {
public void run() {
for (int i=0; i<100; i++) {
System.out.println(Thread.currentThread().getName() + "," + i);
if (i % 10 == 0) {
try {
//睡眠 100 毫秒,主要是放弃 CPU 的使用,将 CPU 时间片交给其他线程使用
Thread.sleep(100);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
停止一个线程
- 如果我们的线程正在睡眠,可以采用 interrupt 进行中断
- 通常定义一个标记,来判断标记的状态停止线程的执行
yield
它与 sleep()类似,只是不能由用户指定暂停多长时间,并且 yield()方法只能让同优先级的线程有执行的机会,采用 yieid 可以将 CPU 的使用权让给同一个优先级的线程
join
当前线程可以调用另一个线程的 join 方法,调用后当前线程会被阻塞不再执行,直到被调用的线程执行完毕,当前线程才会执行
synchronized
线程同步,指某一个时刻,指允许一个线程来访问共享资源,线程同步其实是对对象加锁,如果对象中的方法都是同步方法,那么某一时刻只能执行一个方法,采用线程同步解决以上的问题,我们只要保证线程一操作 s 时,线程 2 不允许操作即可,只有线程一使用完成 s 后,再让线程二来使用 s 变量
- 异步编程模型 : t1线程执行t1的,t2线程执行t2的,两个线程之间谁也不等谁.
- 同步编程模型 : t1线程和t2线程执行,t2线程必须等t1线程执行结束之后,t2线程才能执行,这是同步编程模型.
-
- 什么时候要用同步呢?为什么要引入线程同步呢?
- 1.为了数据的安全,尽管应用程序的使用率降低,但是为了保证数据是安全的,必须加入线程同步机制.
- 线程同步机制使程序变成了(等同)单线程.
- 2.什么条件下要使用线程同步?
- 第一: 必须是多线程环境
- 第二: 多线程环境共享同一个数据.
- 第三: 共享的数据涉及到修改操作.
//synchronized 是对对象加锁
//采用 synchronized 同步最好只同步有线程安全的代码
//可以优先考虑使用 synchronized 同步块
//因为同步的代码越多,执行的时间就会越长,其他线程等待的时间就会越长
//影响效率
public synchronized void run() {
//使用同步块
synchronized (this) {
for (int i=0; i<10; i++) {
s+=i;
}
System.out.println(Thread.currentThread().getName() + ", s=" + s);
s = 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
public class SynchronizedTest {
public static void main(String[] args) {
SynchronizeTest1();
}
private static void SynchronizeTest1() {
Account account=new Account("Actno-001",5000.0);
Thread t1=new Thread(new Processor(account));
Thread t2=new Thread(new Processor(account));
t1.start();
t2.start();
}
}
/**
* 取款线程
*/
class Processor implements Runnable{
Account act;
Processor(Account act){
this.act=act;
}
@Override
public void run() {
act.withdraw(1000.0);
System.out.println("取款1000.0成功,余额: "+act.getBalance());
}
}
class Account {
private String actno;
private double balance;
public Account() {
super();
}
public Account(String actno, double balance) {
super();
this.actno = actno;
this.balance = balance;
}
public String getActno() {
return actno;
}
public void setActno(String actno) {
this.actno = actno;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
/**
* 对外提供一个取款的方法 对当前账户进行取款操作
*/
public void withdraw(double money) {
//把需要同步的代码,放到同步语句块中.
//遇到synchronized就找锁,找到就执行,找不到就等
/**
* 原理: t1线程和t2线程
* t1线程执行到此处,遇到了synchronized关键字,就会去找this的对象锁,
* 如果找到this对象锁,则进入同步语句块中执行程序,当同步语句块中的代码执行结束之后,
* t1线程归还this的对象锁.
*
* 在t1线程执行同步语句块的过程中,如果t2线程也过来执行以下代码,也遇到synchronized关键字,
* 所以也去找this对象锁,但是该对象锁被t1线程持有,只能在这等待this对象的归还.
*
* synchronized关键字添加到成员方法上,线程拿走的也是this的对象锁.
*
*/
synchronized (this) {
double after = balance - money;
try {
//延迟
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//更新
this.setBalance(after);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
public class SynchronizedTest2 {
public static void main(String[] args) throws InterruptedException {
MyClass mc1=new MyClass();
MyClass mc2=new MyClass();
Thread t1=new Thread(new Runnable1(mc1));
Thread t2=new Thread(new Runnable1(mc2));
t1.setName("t1");
t2.setName("t2");
t1.start();
//延迟,保证t1先执行
Thread.sleep(1000);
t2.start();
}
}
class Runnable1 implements Runnable{
MyClass mc;
Runnable1(MyClass mc){
this.mc=mc;
}
@Override
public void run() {
if("t1".equals(Thread.currentThread().getName())){
MyClass.m1();//因为是静态方法,用的还是类锁,和对象锁无关
}
if("t2".equals(Thread.currentThread().getName())){
MyClass.m2();
}
}
}
class MyClass{
//synchronized添加到静态方法上,线程执行此方法的时候会找类锁,类锁只有一把
public synchronized static void m1(){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m1()............");
}
/**
* m2()不会等m1结束,因为该方法没有被synchronized修饰
*/
// public static void m2(){
// System.out.println("m2()........");
// }
/**
* m2方法等m1结束之后才能执行,该方法有synchronized
* 线程执行该方法需要"类锁",而类锁只有一个.
*/
public synchronized static void m2(){
System.out.println("m2()........");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
死锁
public class DeadLock {
public static void main(String[] args) {
Object o1 = new Object();
Object o2 = new Object();
Thread t1 = new Thread(new T1(o1, o2));
Thread t2 = new Thread(new T2(o1, o2));
t1.start();
t2.start();
}
}
class T1 implements Runnable {
Object o1;
Object o2;
T1(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
@Override
public void run() {
synchronized (o1) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2) {
}
}
}
}
class T2 implements Runnable {
Object o1;
Object o2;
T2(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
@Override
public void run() {
synchronized (o2) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1) {
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
守护线程
从线程分类上可以分为:用户线程(以上讲的都是用户线程),另一个是守护线程。守护线程是这样的,所有的用户线程结束生命周期,守护线程才会结束生命周期,只要有一个用户线程存在,那么守护线程就不会结束,例如 java 中著名的垃圾回收器就是一个守护线程,只有应用程序中所有的线程结束,它才会结束。
- 其它所有的用户线程结束,则守护线程退出!
- 守护线程一般都是无限执行的.
public class DaemonThread {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable2());
t1.setName("t1");
// 将t1这个用户线程修改成守护线程.在线程没有启动时可以修改以下参数
t1.setDaemon(true);
t1.start();
// 主线程
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "----->" + i);
Thread.sleep(1000);
}
}
}
class Runnable2 implements Runnable {
@Override
public void run() {
int i = 0;
while (true) {
i++;
System.out.println(Thread.currentThread().getName() + "-------->" + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
设置为守护线程后,当主线程结束后,守护线程并没有把所有的数据输出完就结束了,也即是说守护线程是为用户线程服务的,当用户线程全部结束,守护线程会自动结束
Timer.schedule()
/**
* 关于定时器的应用 作用: 每隔一段固定的时间执行一段代码
*/
public class TimerTest {
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span>(String[] args) <span class="hljs-keyword">throws</span> ParseException {
<span class="hljs-comment">// 1.创建定时器</span>
Timer t = <span class="hljs-keyword">new</span> Timer();
<span class="hljs-comment">// 2.指定定时任务</span>
t.schedule(<span class="hljs-keyword">new</span> LogTimerTask(), <span class="hljs-keyword">new</span> SimpleDateFormat(<span class="hljs-string">"yyyy-MM-dd HH:mm:ss SSS"</span>).parse(<span class="hljs-string">"2017-06-29 14:24:00 000"</span>), <span class="hljs-number">10</span> * <span class="hljs-number">1000</span>);
}
}
// 指定任务
class LogTimerTask extends TimerTask {
<span class="hljs-annotation">@Override</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>() {
System.out.println(<span class="hljs-keyword">new</span> SimpleDateFormat(<span class="hljs-string">"yyyy-MM-dd HH:mm:ss SSS"</span>).format(<span class="hljs-keyword">new</span> Date()));
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22