进程与线程
- 定义
进程: 具有一定独立功能的程序关于数据集合上的一次运行程序,它是系统进行资源分配和管理的独立单位
线程:进程中独立运行的子任务,是CPU调度和分配的基本单位 - 关系
如果进程只有一个线程,称之为单线程;有两个及以上多个线程则称为多线程。进程拥有系统分配的软硬件资源,即拥有独立且完整的虚拟地址空间,由进程控制块(PCB)、程序代码、程序代码进行操作的数据结构集组成,各个进程之间相互独立;线程是进程的组成部分,它只代表了CPU执行的过程,而没有发生进程所拥有的资源的变化,他们共享同一进程下的资源,其本身没有自己的地址空间,准确的说,只拥有一点必不可少的资源(程序计数器、栈、寄存器)。
附:(单线程:Node.Js的解决网络I/O的创新方式,单线程+事件队列+异步响应机制;多线程:传统的Java Web的处理方式,当用户向服务器发送请求、建立连接时,通过多线程的方式,处理用户的请求)
“栗子”
假设你使用的是windows系统,则打开任务管理器,你会发现一大堆后台进程,如图1.1;例如WeChat和QQ,这两个进程之间相对独立,拥有不同的地址空间,当WeChat进程就绪、运行或阻塞,QQ进程仍然正常运行,不受干扰;对于WeChat进程,其中假设你正在上传一段视频给你的女票的同时,你又在和你的妈妈进行视频聊天,那么此时微信的文件上传线程、视频聊天线程一定在工作,那么此时他们都在利用进程的资源,例如内存资源、摄像头资源、文件资源等等。
- 定义
实现一个多线程的类
java的jdk包中自带了对多线程的支持,我们可以通过两种方式实现:- 继承Thread类
实现Runnable接口
public class Thread implements Runnable { //code public Thread(Runnable target) { //code } //code }
我们发现Thread类是实现Runnable接口的,另外在其构造方法中可以传入Runnable的实现对象,这主要是因为为java的单继承多实现的机制。下面实现一个简单的多线程类并测试:
public class MyThread1 extends Thread { public void run() { super.run(); System.out.println("MyThread1 extends Thread"); } }
public class MyThread2 implements Runnable { public void run() { System.out.println("MyThread2 implements Runnable"); } }
public class TestThread { public static void main(String[] args) throws InterruptedException { Thread myThread1=new MyThread1(); Runnable myThread2=new MyThread2(); Thread thread=new Thread(myThread2); myThread1.start(); thread.start(); }
运行结果:
此时,你可能会问为什么使用start方法,不是有run方法吗,不能直接调用吗?那我们就用代码试试吧!将上面的MyThread1及MyThread2的类中的打印方法分别变为:
System.out.println("MyThread1 extends Thread --------Thread's Name:"+Thread.currentThread().getName());
System.out.println("MyThread2 implements Runnable --------Thread's Name:"+Thread.currentThread().getName());
再将TestThread改为:
public class TestThread { public static void main(String[] args) throws InterruptedException { System.out.println("执行main方法 --------Thread's Name"+Thread.currentThread().getName()); Thread myThread1=new MyThread1(); Runnable myThread2=new MyThread2(); Thread thread=new Thread(myThread2); myThread1.start(); thread.start(); myThread2.run(); } }
运行结果:
分析一下,执行main方法时,首先执行的是main主线程,然后再执行了直接调用的run方法,接着执行MyThread1对象的start方法,以及MyThread2对象的start方法,why?至少在这里我们可以发现start方法不是同步执行的,接下来,就看看源码呗!!!
public synchronized void start() { /** This method is not invoked for the main method thread or "system" group threads created/set up by the VM. Any new functionality added to this method in the future may have to also be added to the VM. A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started so that it can be added to the group's list of threads and the group's unstarted count can be decremented. */ group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } }
public void run() { if (target != null) { target.run(); }
看源码中,target是Thread的属性之一,代表构造注入的Runable实现对象。使用start方法时,将线程加入线程规划器,并通知线程规划器该线程准备就绪,java会调用native方法通过C语言进行系统调用,接下来由系统安排一个时间执行调用其run方法对,具有异步执行的效果;而在run方法中可以看到,采取的是直接执行run方法的方式,则线程对象不由线程规划器进行管理,而是由主线程进行调用,是同步执行的。
实例变量与线程安全
不共享数据的情况
public class MyThread1 extends Thread { private int count=5; public MyThread1(String name) { super(); this.setName(name); } public void run() { super.run(); while (count>0){ count--; System.out.println("Thread's Name: "+Thread.currentThread().getName()+"计算得,count="+count); } } }
public static void main(String[] args) throws InterruptedException { MyThread1 myThread1=new MyThread1("A"); MyThread1 myThread11=new MyThread1("B"); MyThread1 myThread12=new MyThread1("C"); myThread1.start(); myThread11.start(); myThread12.start(); } }
运行结果:
共享数据的情况
public class MyThread1 extends Thread { private int count=5; public MyThread1() { super(); } public MyThread1(String name) { super(); this.setName(name); } public void run() { super.run(); count--; System.out.println("Thread's Name: "+Thread.currentThread().getName()+"计算得,count="+count); } }
public class TestThread { public static void main(String[] args) throws InterruptedException { MyThread1 myThread1=new MyThread1(); Thread thread0=new Thread(myThread1,"A"); Thread thread1=new Thread(myThread1,"B"); Thread thread2=new Thread(myThread1,"C"); thread0.start(); thread1.start(); thread2.start(); }
运行结果:
可以发现共享数据的情况下,线程B和C对共享变量count处理时,产生“非线程安全问题”,那怎么让线程按顺序对共享变量进行操作呢?
public class MyThread1 extends Thread { private int count=5; public MyThread1() { super(); } public MyThread1(String name) { super(); this.setName(name); } synchronized public void run() { super.run(); count--; System.out.println("Thread's Name: " + Thread.currentThread().getName() + "计算得,count=" + count); }
运行结果:
可以看出我们可以通过对需要同步执行的方法前,加入synchronized关键字,synchronized可以在任意对象和方法上加锁,加锁后的代码段称之为“临界区”,当线程访问run方法时,先判断其是否上锁,如果上锁,说明有其他线程正在调用run方法,就等待临界区解锁再执行,已达到同步、数据安全的目的。
下一篇,我会对线程的生命周期和相应的java代码加以说明。如有偏差或错误,请给予纠正,在此谢谢你的阅读!(我的天,csdn的markdown调了半天… …)