java底层学习(一)——多线程

一、进程与线程

进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体。它不只是程序的代码,还包括当前的活动,通过程序计数器的值和处理寄存器的内容来表示。

线程:线程是位于进程中,负责某个独立运行资格的空间(进程是线a程分配的一块独立的空间)。

多线程:在一个进程中可以开启多个线程,让多个线程同时去完成某项任务。使用多线程的目的是为了提高程序的执行效率。

简单说就是一个程序运行以后至少得有一个进程,但是可以有n个线程

单线程程序有多个程序只能依次进行,只有当前一个程序执行结束以后才能执行下一程序。

每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。每个线程都可以或不可以标记为一个守护程序。当某个线程中运行的代码创建一个新 Thread 对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程才是守护程序。

当 Java 虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的 main 方法)。Java 虚拟机会继续执行线程,直到下列任一情况出现时为止:

调用了 Runtime 类的 exit 方法,并且安全管理器允许退出操作发生。

非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,还是通过抛出一个传播到 run 方法之外的异常。

二、多线程

多线程程序可以允许多个程序同时进行,但在CPU在线程中做时间片的切换,CPU负责程序的执行,但是同一时间只能执行一个线程。CPU不停在多个程序之间高速切换。实现多线程的方法有三种,继承Thread类,实现Runnable接口,使用线程池。

2.1继承Thread类

package com.ahrtolia.ThreadTest;

import java.util.Random;

public class ThreadTest extends Thread{
	
	String flag;

	public ThreadTest(String flag) {
		// TODO Auto-generated constructor stub
		this.flag = flag;
	}
	
	public void run() {
		String name = Thread.currentThread().getName();
		System.out.println("线程"+name+"开始工作");
		Random random = new Random();
		for(int i=0;i<5;i++) {
			try {
				Thread.sleep(random.nextInt(10)*100);
				System.out.println(name+"====="+flag);
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Thread T0 = new ThreadTest("0");
		Thread T1 = new ThreadTest("1");
		
		T0.start();
		T1.start();
	}

}

运行结果:

线程Thread-1开始工作
线程Thread-0开始工作
Thread-1=====1
Thread-0=====0
Thread-1=====1

 

调用线程需要使用start()方法而不是run方法,使用run方法只是调用方法,实际执行的还是main线程,调用start方法可以明显看到线程争抢。

new Thread的弊端如下: 
1.每次new Thread新建对象性能差。 
2.线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。 
3.缺乏更多功能,如定时执行、定期执行、线程中断。

2.2实现runnable接口

package com.ahrtolia.ThreadTest;

import java.util.Random;

public class RunnableTest implements Runnable{

	int x;
	
	public RunnableTest(int x) {
		// TODO Auto-generated constructor stub
		this.x = x;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		String name = Thread.currentThread().getName();
		System.out.println("线程"+x+"开始执行");
		Random random = new Random();
		for(int i=0;i<5;i++) {
			try {
				Thread.sleep(random.nextInt(10)*100);
				System.out.println(name+"----------"+x);
			}catch (InterruptedException e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}
		
	}
	public static void main(String[] args) {
			// TODO Auto-generated method stub
		Thread T0 = new Thread(new RunnableTest(1), "线程1");
		Thread T1 = new Thread(new RunnableTest(2), "线程2");
		
		T0.start();
		T1.start();
		}

}

执行结果如下:

线程2开始执行
线程1开始执行
线程1----------1
线程2----------2
线程1----------1
线程2----------2
线程1----------1
线程1----------1
线程1----------1
线程2----------2
线程2----------2
线程2----------2

也可以明显看线程争抢。
 

2.3实现Callable接口

需要实现Callable接口类MyThreadImplementCallable;

创建一个类的对象:MyThreadImplementCallable callable = new MyThreadImplementCallable(“测试”);

由Callable创建一个FutureTask对象: 
FutureTask  futureTask = new FutureTask(callable); 
注意:FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口。

有FutrueTask创建一个Thread对象:

Thread thread = new Thread/(Futrue Task);

启动线程:Thread.start();

获取任务线程执行结果:

FutrueTask.get();

注意:实现Callable接口的线程可以获得任务线程的执行结果;实现Runnable接口的线程无法获取任务线程执行的结果。

package com.ahrtolia.ThreadTest;

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;


public class CallableTest implements Callable<String>{

	String name;
	public CallableTest(String name) {
		// TODO Auto-generated constructor stub
		this.name = name;
	}
	
	@Override
	public String call() throws Exception {
		// TODO Auto-generated method stub
		Thread thread = Thread.currentThread();
		System.out.println(thread.getName()+"开始工作--------");
		Random random = new Random();
		Thread.sleep(random.nextInt(5)*100);
		return name+"执行完成";
	}
	
	public static void main(String[] args) throws Exception {
			// TODO Auto-generated method stub
		
		CallableTest callable = new CallableTest("测试");
		FutureTask<String> futureTask = new FutureTask<String>(callable);
		Thread thread = new Thread(futureTask);
		
		thread.start();
		String result = futureTask.get();
		System.out.println("线程的执行结果"+result);
		}

}

运行结果:

Thread-0开始工作--------
线程的执行结果测试执行完成

 

2.4线程池:

在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。

Java提供的四种线程池的好处在于: 
1.重用存在的线程,减少对象创建、消亡的开销,性能佳。 
2.可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。 
3.提供定时执行、定期执行、单线程、并发数控制等功能。

Executors创建线程池

Java中创建线程池很简单,只需要调用Executors中相应的便捷方法即可,比如Executors.newFixedThreadPool(int nThreads),但是便捷不仅隐藏了复杂性,也为我们埋下了潜在的隐患(OOM,线程耗尽)。

线程池具体实现方法见下篇

 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值