进程与线程
进程
- 进程就是内存中运行的程序
![](https://i-blog.csdnimg.cn/blog_migrate/04cbe1ea84726bf1f91580a3e5fabc8e.png)
- 并发:并发就是同一时间段cpu执行多个进程,执行完一个在执行下一个
- 并行:并行就是同一时刻cpu执行多个进程
线程
线程是进程的一部分,是程序进入cpu的一条路径,一个进程中可以并发多个线程,每条线程并行执行不同的任务,多线程值得就是就是在同一时刻能够执行多个线程,单线程指的是同一时刻只能执行一个线程,线程示例如下:
![](https://i-blog.csdnimg.cn/blog_migrate/91396ef150e672c7503ce633bdd0e186.png)
- 守护线程
守护线程就是一类类似于垃圾回收机制的线程,因为进程会在所有线程结束后才消失,但是垃圾回收机制线程不会结束就会导致进程也不会结束,所以需要将它们标志了守护线程,Java 虚拟机会在所有非守护线程终止后结束进程。守护线程地创建如下:
Thread thread = new WatchingThread();
thread.setDaemon(true);
thread.start();
线程调度和线程状态
线程调度值的是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.示例,该示例将会不停地输出两个线程地名字:
//继承Thread类重写run方法
package TreadTest;
public class MyThread extends Thread{
@Override
public void run() {
while(true) System.out.println(currentThread().getName()+"运行");
}
}
//创建多线程
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方法,在其中定义复制文件即可
//创建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;
@Override
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();
}
}
}
结果如下,多线程创建成功
![](https://i-blog.csdnimg.cn/blog_migrate/ea84afd7b0f7b84ab1a2f27f774f4066.png)
通过实现Runable接口实现多线程
这种方法与与继承Thread类差不多,编写一个类实现 Runnable 接口,并实现该接口中的 run() 方法。然后创建 Runable 实现类的实例,并以此实例作为 Thread 的 target 来创建 Thread 对象,示例如下:
//实现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
为了解决某些因为数据同步调用而产生地线程不安全问题,我们需要使用监视器实现线程同步,线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。监视器的原理如下::
![](https://i-blog.csdnimg.cn/blog_migrate/fd7cbf4c09e6ddc5373f3c67c460dbd5.png)
![](https://i-blog.csdnimg.cn/blog_migrate/219d5e8ada47667b91cc08e2ca743e17.png)
Synchronized与static类似,Synchronized代码块和Synchronized方法作用相同,形式不同,作用范围不同:
e.g.示例如下:
//Synchronized代码块
package TreadTest;
public class TicketThread implements Runnable{
private int count = 10;
public void run(){
while(true){
synchronized (this){
if (count > 0) {
try {Thread.sleep(1000);}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"窗口卖出了第"+count--+"张票");
}
else {
System.out.println("票已经售完了!");
break;
}
}
}
}
}
//Synchronized方法
package TreadTest;
public class TicketThread implements Runnable{
private int count = 10;
public synchronized void run(){
while(true){
if (count > 0) {
try {Thread.sleep(1000);}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"窗口卖出了第"+count--+"张票");
}
else {
System.out.println("票已经售完了!");
break;
}
}
}
}
定时器
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定时器调用,示例如下:
//继承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(){
@Override
public void run() {
System.out.println("延时5s");;
}
},2000);
//延时后定期执行定时器
timer.schedule(new TimeTask(){
@Override
public void run() {
System.out.println("一秒后执行任务,之后每隔一秒执行一次");
}
},2000,1000);
//定期执行定时器
timer.schedule(new TimeTask(){
@Override
public void run() {
System.out.println("每隔3s执行一次");
}
},new Date(),3000);
}
}
部分运行结果如下:
![](https://i-blog.csdnimg.cn/blog_migrate/be8e93972b7c4a2dca11061f0b2f179c.png)