进程和线程
什么是进程:进程是系统进行资源分配单位,是操作系统结构的基础,比如我们在使用微信聊天,当我们启动微信就相当于我们启动了一个进程。
什么是线程:线程是cpu资源调度的最小单位。一个进程最少有一个线程,
通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度,从而显著提高系统资源的利用率和吞吐量。
线程创建的方法 :4种方式
一、编写一个类,直接 继承
java.lang.Thread
,重写run方法
。
- 怎么创建线程对象? new继承线程的类。
- 怎么启动线程呢? 调用线程对象的
start()
方法。
二、编写一个类,实现
java.lang.Runnable
接口,实现run方法
。
- 怎么创建线程对象? new Thread 传入可运行的类/接口。
- 怎么启动线程呢? 调用线程对象的
start()
方法。- 也可以使用匿名内部类方式创建:
三、编写一个类,实现Callable接口(JDK5.0新增)实现call方法
该方法效率较低,因为在获取线程的执行结果的时候,当前线程受阻塞。
但是可以拿到线程的返回结果
//1.创建一个实现Callable的实现类 class NumThread implements Callable{ //2.实现call方法,将此线程需要执行的操作声明在call()中 @Override public Object call() throws Exception { int sum = 0; for(int i = 1;i <= 100;i++){//快捷方式:100.for if(i % 2 == 0){ System.out.println(i); sum += i; } } return sum; } } public class ThreadNew { public static void main(String[] args) { //3.创建Callable接口实现类的对象 NumThread numThread = new NumThread(); //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象 FutureTask futureTask = new FutureTask(numThread); //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start() new Thread(futureTask).start(); try { //6.获取Callable中call方法的返回值 //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。 Object sum = futureTask.get(); System.out.println("总和为:" + sum); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
四、使用线程池创建
对经常大量创建销毁、使用量特别大的资源,比如并发情况下的线程,对性能的影响很大
可以提前创建多个线程,放入线程池,使用时候直接获取,使用完之后放回池中。以避免频繁的创建销毁、实现重复利用率
好处呢显然就是1、提高响应速度 2、降低资源消耗 3、便于线程管理
线程池各项参数:
corePoolSize 代表核心线程数
maxinumPoolSize 代表的是最大线程数
keepAliveTime 表示超出核心线程数之外的线程的空闲存活时间
workQueue 用来存放待执行的任务
ThreadFactory 实际上是一个线程工厂,用来生产线程执行任务
Handler 任务拒绝策略,有两种情况,第一种是当我们调用 shutdown 等方法关闭线程池后,这 时候即使线程池内部还有没执行完的任务正在执行,但是由于线程池已经关闭,我们再继续想线程 池提交任务就会遭到拒绝。另一种情况就是当达到最大线程数,线程池已经没有能力继续处理新提 交的任务时,这是也就拒绝。
public class ThreadPool { public static void main(String[] args) { // ExecutorService executorService = Executors.newFixedThreadPool(10); //制造一个线程池,里面有10个线程 //注意Executors.newFixedThreadPool是一个接口,需要转换成实现类 ThreadPoolExecutor executorService = (ThreadPoolExecutor)Executors.newFixedThreadPool(10); //设置线程池的属性 executorService.setCorePoolSize(10); //代表核心线程数,也就是正常情况下创建工作的线程数,这些线程创建后并不会消除,而是一种常驻线程 executorService.setMaximumPoolSize(10); //代表的是最大线程数 executorService.setKeepAliveTime(1000, TimeUnit.MILLISECONDS); // 表示超出核心线程数之外的线程的空闲存活时间 executorService.execute(new NumberThread());//适合适用于Runnable executorService.execute(new NumberThread2());//适合适用于Runnable // executorService.submit(); //适合适用于Callable executorService.shutdown(); //关闭线程池 } }
线程池执行流程:
当多个线程同时对数据进行访问和修改时,可能会出现数据不准确等问题,使用synchroized加互斥锁始终保持同时只有一个线程能够访问,保证数据安全。
synchroized关键字互斥锁
互斥锁
1.每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
2.关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,。表明该对象在任一时刻只能由一个线程访问
3.同步的局限性:导致程序的执行效率要降低
4.同步方法((非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)5.同步方法(静态的)的锁为当前类本身。
方法:
1、同步代码块
synchronized (对象) { //得到对象的锁,才能操作同步代码,只要是 同一对象都行
//需要同步的代码
}
2、放在方法上
非静态同步方法锁默认是加到this上
public synchronized void lock(){ //表示方法为同步方法
//需要背同步的代码
}
3、放在静态方法上
注意静态同步方法的锁是加在类名.class,里面代码块也需要类名.class
public static void lock(){ //表示方法为静态同步方法
synchronized(类名.class){
//同步代码块内容
}
}