进程与线程的概念
进程: 操作系统中一个程序的执行周期称为一个进程。(是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体)
windows是一个多进程的操作系统。
线程: :一个程序同时执行多个任务。通常,每一个任务就称为一个线程。与进程相比较,线程更"轻量级",创建、 撤销一个线程比启动一个新进程开销要小的多。没有进程就没有线程,进程一旦终止,其内的线程也将不复存在。
主方法是一个线程。
多进程与多线程区别: 本质区别在于,每个进程拥有自己的一整套变量,而线程则共享数据。共享变量使得线程之 间的通信比进程之间通信更有效、更方便。
多线程表现:一个浏览器应用可以同时下载多个图片、音乐;一个Web服务器需要同时 处理多个并发的请求。这些都是多线程的应用
进程与线程比较:
1.与进程相比较,线程更加“轻量级”,创建、撤销一个线程比启动、撤销一个进程开销要小的多。一个进程中的所有线程共享此进程的所有资源
2.没有进程就没有线程,进程一旦终止,其内的线程也将不复存在
3.进程是操作系统资源调度的基本单位。进程可以独享资源;线程需要依托进程提供的资源,无法独立申请操作系统资源,是OS任务执行的基本单位。
线程状态
Java多线程实现
继承Thread类实现多线程
java.lang.Thread是一个线程操作的核心类。新建一个线程简单的方法就是直接继承Thread类,而后覆写该类中的 run()方法(就相当于主类中的main方法)
定义线程的主体类
class MyThread extends Thread{//线程主体类
private String title;
public MyThread(String title){
this.title = title;
}
@Override
public void run(){//所有线程从此处开始执行
for(int i = 0; i < 10; i++){
System.out.println(this.title + ",i = " + i);
}
}
}
当现在有了线程的主体类之后,很自然我们就会想到产生线程类的实例化对象而后调用run()方法。
观察调用run()方法
public class Test{
public static void main(String[] args){
MyThread myThread1 = new MyThread("abaka");
MyThread myThread2 = new MyThread("abaka1");
MyThread myThread3 = new MyThread("abaka2");
myThread1.run();
myThread2.run();
myThread3.run();
}
}
运行结果:
abaka,i = 0
abaka,i = 1
abaka,i = 2
abaka,i = 3
abaka,i = 4
abaka,i = 5
abaka,i = 6
abaka,i = 7
abaka,i = 8
abaka,i = 9
abaka1,i = 0
abaka1,i = 1
abaka1,i = 2
abaka1,i = 3
abaka1,i = 4
abaka1,i = 5
abaka1,i = 6
abaka1,i = 7
abaka1,i = 8
abaka1,i = 9
abaka2,i = 0
abaka2,i = 1
abaka2,i = 2
abaka2,i = 3
abaka2,i = 4
abaka2,i = 5
abaka2,i = 6
abaka2,i = 7
abaka2,i = 8
abaka2,i = 9
这个时候只是做了一个顺序打印,和多线程一点关系都没有。正确启动多线程的方式是调用Thread类中的start()方 法。
启动多线程:public synchronized void start()此方法会自动调用线程的run()方法
正确启动多线程
public class Test{
public static void main(String[] args){
MyThread myThread = new MyThread("abaka");
MyThread myThread1 = new MyThread("abaka1");
MyThread myThread2 = new MyThread("abaka2");
myThread.start();
myThread1.start();
myThread2.start();
}
}
此时再次执行代码发现,所有的线程对象变味了交替执行。
无论那种方法实现多线程,线程启动一定调用Thread类提供的start()方法!
线程的start方法只能调用一次,多次调用会java.lang.IllegalThreadStateException
Java线程创建路径:
start(Java方法) -> strat0(JVM) -> 进行资源调度,系统分配(JVM) -> run(java方法)执行线程的具体操作任务
Runnable()接口实现多线程
Thread类的核心功能是进行线程的启动。如果一个类为了实现多线程直接去继承Thread类就会有但继承局限。在 java中又提供有另外一种实现模式:Runnable接口。
Runnable接口:
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
利用Runable接口实现线程主体类
class MyThread implements Runnable{
private String title;
public MyThread(String title){
this.title = title;
}
@Override
public void run(){
for(int i = 0; i < 5; i++){
System.out.println(this.title + ",i = " + i);
}
}
}
此时MyThread类继承的不再是Thread类而实现了Runnable接口,虽然解决了单继 承局限问题,但是没有start()方法被继承了。那么此时就需要关注Thread类提供的构造方法。
Thread类提供的构造方法
public Thread(Runnable target)
可以接收Runnable接口对象
启动多线程
public class Test{
public static void main(String[] args){
MyThread myThread = new MyThread("abaka");
MyThread myThread1 = new MyThread("abaka1");
MyThread myThread2 = new MyThread("abaka2");
new Thread(myThread).start();
new Thread(myThread1).start();
new Thread(myThread2).start();
}
}
多线程的启动永远都是Thread类的start()方法
对于此时的Runnable接口对象可以采用匿名内部类或者Lambda表达式来定义。
使用匿名内部类进行Runnable对象创建
public class Test{
public static void main(String[] args){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello abaka");
}
}).start();
}
}
使用Lamdba表达式进行Runnable对象创建
public static void main(String[] args){
nnable runnable = () -> System.out.println("hello abaka");
new Thread(runnable).start();
}
}
继承Thread类与实现Runnable接口的区别
I.Thread类与自定义线程类(实现了Runnable接口)。是一个典型的代理设计模式。Thread类负责辅助真实业务操作(资源调度,创建线程并启动)自定义线程类负责真实业务的实现(run方法具体要做啥事)
II.使用Runnable接口实现的多线程程序类可以更好的描述共享的概念
使用Thread实现数据共享(产生若干线程进行同一数据的处理操作)
class MyThread extends Thread{
private int ticket = 5;
@Override
public void run(){
while(this.ticket > 0){
System.out.println("剩余票数:" + this.ticket -- );
}
}
}
public class Test{
public static void main(String[] args){
new MyThread().start();
new MyThread().start();
new MyThread().start();
}
}
此时启动三个线程实现卖票处理。结果变为了卖各自的票
使用Runnable实现共享
class MyThread implements Runnable{
private int ticket = 5;
@Override
public void run(){
while(this.ticket > 0){
System.out.println("剩余票数: " + this.ticket -- );
}
}
}
public class Test{
public static void main(String[] args){
MyThread myThread = new MyThread();
new Thread(myThread).start();
new Thread(myThread).start();
}
}
运行结果:
剩余票数: 5
剩余票数: 4
剩余票数: 3
剩余票数: 2
剩余票数: 1
Runnable实现的多线程的程序类可以更好的描述出程序共享的概念
Callable实现多线程
从JDK1.5开始追加了新的开发包:java.uti.concurrent。这个开发包主要是进行高并发编程使用的,包含很多在高并 发操作中会使用的类。在这个包里定义有一个新的接口Callable
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Runnable中的run()方法没有返回值,它的设计也遵循了主方法的设计原则:线程开始了就别回头。但是很多时候需 要一些返回值,例如某些线程执行完成后可能带来一些返回结果,这种情况下就只能利用Callable来实现多线程。
使用Callable定义线程主体类。
class MyThread implements Callable<String> {
private int ticket = 5;
@Override
public String call() throws Exception{
while(this.ticket > 0){
System.out.println("剩余票数:" + this.ticket --);
}
return "票卖完";
}
}
启动并取得多线程的执行结果
public class Test{
public static void main(String[] args) throws InterruptedException, ExecutionException{
FutureTask<String> task = new FutureTask<>(new MyThread());
new Thread(task).start();
new Thread(task).start();
System.out.println(task.get());
}
}
其中FutureTask task = new FutureTask<>(new MyThread());这句是因为callable的继承关系如下图:
其中箭头表示继承关系。