进程与线程
进程
- 进程就是内存中运行的程序
- 并发:并发就是同一时间段cpu执行多个进程,执行完一个在执行下一个
- 并行:并行就是同一时刻cpu执行多个进程
线程
线程是进程的一部分,是程序进入cpu的一条路径,一个进程中可以并发多个线程,每条线程并行执行不同的任务,多线程值得就是就是在同一时刻能够执行多个线程,单线程指的是同一时刻只能执行一个线程,线程示例如下:
- - 守护线程 守护线程就是一类类似于垃圾回收机制的线程,因为进程会在所有线程结束后才消失,但是垃圾回收机制线程不会结束就会导致进程也不会结束,所以需要将它们标志了守护线程,Java 虚拟机会在所有非守护线程终止后结束进程。守护线程地创建如下:1 | Thread thread = new WatchingThread(); |
线程调度和线程状态
线程调度值的是cpu执行线程的实际情况,有以下两种:
- 分时调度
分时调度是线程轮流平均使用cpu,每个线程占用cpu时间相同 - 抢占式调度
抢占式调度是按照线程的优先级,来决定cpu先执行哪个进程,如果优先级相同则会随机选择,即线程随机性
线程状态
每个Java程序都有一个缺省的主线程,Application的主线程是main()方法的执行过程;Applet的主线程是通过浏览器加载并执行java小程序。Java使用Thread类及其子类表示线程5中状态:
新建(Newborn)->就绪(Runnable)->运行(Running)->阻塞(Blocked)->死亡(Dead)
线程实现
通过继承Thread类来创建并启动多线程:
- 定义Thread类的子类并重载线程运行体run() 方法,run()方法中定义线程需要完成的任务。
- 创建 Thread 子类的实例,即创建了线程对象。
- 使用线程对象的 start() 方法来启动该线程。
e.g.示例,该示例将会不停地输出两个线程地名字:1
2
3
4
5
6
7
8
9//继承Thread类重写run方法
package TreadTest;
public class MyThread extends Thread{
public void run() {
while(true) System.out.println(currentThread().getName()+"运行");
}
}1
2
3
4
5
6
7
8
9
10
11
12
13//创建多线程
package TreadTest;
public class TreadTets {
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
myThread1.setName("thread_1");
MyThread myThread2 = new MyThread();
myThread2.setName("thread_2");
myThread1.start();
myThread2.start();
}
}使用多线程复制文件
在Thread子类重写run方法,在其中定义复制文件即可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//创建Thread子类
package TreadTest;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class MyThread extends Thread {
public MyThread(String source,String target){
this.souce = source;
this.target = target;
}
private String souce;
private String target;
public void run() {
try {
FileInputStream fis = new FileInputStream(souce);
FileOutputStream fos = new FileOutputStream(target);
byte b[] = new byte[1024];
int len;
while ((len = fis.read()) != -1) {
fos.write(b, 0, len);
}
fis.close();
fos.close();
}catch (IOException e){
System.out.println("复制失败");
}
}
}
//创建多线程
package TreadTest;
public class CopyThreadTest {
public static void main(String[] args) {
for(int i=1;i<=3;i++){
String source = "D:/javaTestFile/"+ i + ".txt";
String target = "D:/javaTestFile/"+ i + "(" + i + ")" + ".txt";
MyThread myThread = new MyThread(source,target);
myThread.start();
}
}
}
```
结果如下,多线程创建成功
<div align=center><img src="https://cdn.jsdelivr.net/gh/sunyuzero/cdn/java/java39.png" width=600></div>
### 通过实现Runable接口实现多线程
这种方法与与继承Thread类差不多,编写一个类实现 Runnable 接口,并实现该接口中的 run() 方法。然后创建 Runable 实现类的实例,并以此实例作为 Thread 的 target 来创建 Thread 对象,示例如下:
```java
//实现runable接口
package TreadTest;
public class MyThread_1 implements Runnable{
public void run(){
while(true){
System.out.println("线程:"+ Thread.currentThread().getName()+"运行!");
}
}
}
//创建多线程
package TreadTest;
public class Test_1 {
public static void main(String[] args) {
MyThread_1 target = new MyThread_1();
Thread thread1 = new Thread(target);
thread1.setName("线程——1");
Thread thread2 = new Thread(target);
thread1.setName("线程——2");
thread1.start();
thread2.start();
}
}两种创建多线程方式比较
相比来说,实现Runanle接口更好: - 适合多个相同程序代码的线程去处理同一资源的情况,把线程同程序代码、数据分离较好地体现了面向对象的设计思想。
- 可以避免由于 Java 的单继承特性带来的局限。
- 有利于程序的健壮性,代码能够被多个线程共享。
同步synchronized
为了解决某些因为数据同步调用而产生地线程不安全问题,我们需要使用监视器实现线程同步,线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。监视器的原理如下::
Synchronized能够实现同步的原因是: - 当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例有关的锁。 - 当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。 - 一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放,即该线程退出了synchronized同步方法或代码块。Synchronized与static类似,Synchronized代码块和Synchronized方法作用相同,形式不同,作用范围不同:
e.g.示例如下:
1 | //Synchronized代码块 |
1 | //Synchronized方法 |
定时器
java提供了Timer计时器类,可以方便我们在指定时间执行任务,常用的用法如下:
- timer.schedule(TimerTaskImpl tti,long ms) //延时ms毫秒执行任务
- timer.schedule(TimerTaskImpl tti,long ms1,long ms2) //延时ms1毫秒后每隔ms2毫秒执行任务
- timer.schedule(TimerTaskImpl tti,new Date(),long ms2) //每隔ms2毫秒后执行任务
一般我们需要继承TimerTask并重写run方法,在通过Timer定时器调用,示例如下:部分运行结果如下: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//继承TimerTask并重写run方法
package TreadTest;
import java.util.Timer;
import java.util.TimerTask;
public class TimeTask extends TimerTask{
public void run(){}
}
//实现定时器
package TreadTest;
import java.util.Date;
import java.util.Timer;
public class TimeTest {
public static void main(String[] args) {
Timer timer = new Timer();
//延时定时器
timer.schedule(new TimeTask(){
public void run() {
System.out.println("延时5s");;
}
},2000);
//延时后定期执行定时器
timer.schedule(new TimeTask(){
public void run() {
System.out.println("一秒后执行任务,之后每隔一秒执行一次");
}
},2000,1000);
//定期执行定时器
timer.schedule(new TimeTask(){
public void run() {
System.out.println("每隔3s执行一次");
}
},new Date(),3000);
}
}