JAVA
程序(programm)
概念:是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码。
进程(process)
概念:程序的一次执行过程,或是正在运行的一个程序。
说明:进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
线程(thread)
概念:进程可进一步细化为线程,是一个程序内部的一条执行路径。
说明:线程作为调度和执行的单位,每个线程拥独立的运行栈和程序计数器(pc),线程切换的开销小。
并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事
Java语言的JVM允许程序运行多个线程,它通过java.lang.Thread
类来体现。
Thread类
Thread类的构造器
Thread()
:创建新的Thread对象Thread(String threadname)
:创建线程并指定线程实例名Thread(Runnable target)
:指定创建线程的目标对象,它实现了Runnable接 口中的run方法Thread(Runnable target, String name)
:创建新的Thread对象步骤:
代码实现:
package com.xawl.thread; //①创建一个继承于Thread类的子类 public class MyThread extends Thread { //②重写Thread类的run() --> 将此线程执行的操作声明在run()中 @Override public void run() { for (int i = 0; i < 100; i+=2) { System.out.println("MyThread:"+i); } } public static void main(String[] args) { //③创建Thread类的子类的对象 MyThread t = new MyThread(); //④通过此对象调用start()(注:start有两个作用①启动当前线程 ② 调用当前线程的run()) t.start(); } }
步骤:
代码实现:
package com.xawl.thread; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; //①创建一个实现了Runnable接口的类 public class MyRunnable implements Runnable{ @Override //②实现类去实现Runnable中的抽象方法:run() public void run() { for (int i = 0; i < 100; i+=2) { System.out.println("MyThread:"+i); } } public static void main(String[] args) { //③创建实现类的对象 MyRunnable r = new MyRunnable(); //④将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象 //⑤通过Thread类的对象调用start() new Thread(r).start(); } }
开发中优先选择实现Runnable接口的方式
**两者的联系:**Thread 实现了Runnable接口(public class Thread implements Runnable
)
两者的相同点:
两者的不同点:
void start()
: 启动线程,并执行对象的run()方法run()
: 线程在被调度时执行的操作String getName()
: 返回线程的名称void setName(String name)
:设置该线程名称static Thread currentThread()
: 返回当前线程。在Thread子类中就 是this,通常用于主线程和Runnable实现类static void yield()
:线程让步 暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程 若队列中没有同优先级的线程,忽略此方法join()
:当某个程序执行流中调用其他线程的 join() 方法时,调用线程将 被阻塞,直到 join() 方法加入的 join 线程执行完为止static void sleep(long millis)
:(指定时间:毫秒) 令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后 重排队。stop()
: 强制线程生命期结束,不推荐使用boolean isAlive()
:返回boolean
,判断线程是否还活着线程的优先级:
MAX_PRIORITY
:10MIN _PRIORITY
:1NORM_PRIORITY
:5 -->默认优先级如何获取和设置当前线程的优先级:getPriority()
:获取线程的优先级setPriority(int p)
:设置线程的优先级
说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只当高优先级的线程执行完以后,低优先级的线程才执行。
Java中的线程分为两类:一种是守护线程,一种是用户线程。
它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。
守护线程是用来服务用户线程的,通过在start()方法前调用thread.setDaemon(true)
可以把一个用户线程变成一个守护线程。
Java垃圾回收就是一个典型的守护线程。
若JVM中都是守护线程,当前JVM将退出。
JDK中用Thread.State
类定义了线程的几种状态(下面是总结的几种状态,并不完全对应State
类中的状态)
Java语言使用Thread类 及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的五 种状态:
线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态。
线程同步的目的计算解决线程安全问题
在JAVA中我们有三种方式实现线程同步:①同步代码块②同步方法③Lock锁
synchronized(同步监视器){ //需要被同步的代码 }
说明:
补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类(Class)充当同步监视器。
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法使用synchronized
关键字声明为同步的。
关于同步方法的总结:
从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同 步锁对象来实现同步。同步锁使用Lock对象充当。
java.util.concurrent.locks.Lock
接口是控制多个线程对共享资源进行访问的 工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象 加锁,线程开始访问共享资源之前应先获得Lock对象。
ReentrantLock
类实现了 Lock ,它拥有与synchronized
相同的并发性和 内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock
,可以 显式加锁、释放锁。
class A{ private final ReentrantLock lock = new ReenTrantLock(); public void m(){ lock.lock(); try{ //保证线程安全的代码; } finally{ lock.unlock(); } } } //注意:如果同步代码有异常,要将unlock()写入finally语句块
相同:二者都可以解决线程安全问题
不同:
线程与线程之间不是相互独立的个体,它们彼此之间需要相互通信和协作,最典型的例子生产者-消费者问题就是互相通信的过程,就是线程间的协作。
生产者-消费者问题:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权。因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去。因此一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态。然后等待消费者消费了商品,然后消费者通知生产者队列有空间了。同样地,当队列空时,消费者也必须等待,等待生产者通知它队列中有商品了。
线程通信涉及到的三个方法:
wait()
:一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。notify()
:一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。notifyAll()
:一旦执行此方法,就会唤醒所有被wait的线程。说明:
wait()
,notify()
,notifyAll()
三个方法必须使用在同步代码块或同步方法中。wait()
,notify()
,notifyAll()
三个方法的调用者必须是同步代码块或同步方法中的同步监视器。否则,会出现IllegalMonitorStateException
异常wait()
,notify()
,notifyAll()
三个方法是定义在java.lang.Object
类中。sleep() 和 wait()的异同?
相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
不同点:
Future
接口 可以对具体Runnable
、Callable
任务的执行结果进行取消、查询是 否完成、获取结果等。
FutrueTask
是Futrue
接口的唯一的实现类FutureTask
同时实现了Runnable
, Future
接口。它既可以作为 Runnable
被线程执行,又可以作为Future
得到Callable
的返回值步骤:
Callable
的实现类call
方法,将此线程需要执行的操作声明在call()中Callable
接口实现类的对象Callable
接口实现类的对象作为传递到FutureTask
构造器中,创建FutureTask
的对象FutureTask
的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()Callable
中call
方法的返回值(get()返回值即为FutureTask
构造器参数Callable实现类重写的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++) { 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(); } } }
实现Callable
接口的方式比实现Runnable
接口方式更强大
原因:
经常创建和销毁、使用量特别大的资源,比如并发情况下的线程, 对性能影响很大。
提前创建好多个线程,放入线程池中,使用时直接获取,使用完 放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交 通工具。
使用线程池的好处:
JDK 5.0起提供了线程池相关API:ExecutorService
和 Executors
ExecutorService
:真正的线程池接口。常见子类ThreadPoolExecutor
void execute(Runnable command)
:执行任务/命令,没有返回值,一般用来执行 RunnableFuture submit(Callable task)
:执行任务,有返回值,一般又来执行 Callablevoid shutdown()
:关闭连接池Executors
:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
Executors.newCachedThreadPool()
:创建一个可根据需要创建新线程的线程池Executors.newFixedThreadPool(n)
; 创建一个可重用固定线程数的线程池Executors.newSingleThreadExecutor()
:创建一个只有一个线程的线程池Executors.newScheduledThreadPool(n)
:创建一个线程池,它可安排在给定延迟后运 行命令或者定期地执行。ThreadPoolExecutor
它提供了四种构造函数来创建线程池,其中最为核心的构造函数如下所示:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
这7个参数的含义如下:
corePoolSize
线程池核心线程数。即线程池中保留的线程个数,即使这些线程是空闲的,也不会 被销毁,除非通过ThreadPoolExecutor
的 allowCoreThreadTimeOut(true)
方法开启了核心线程 的超时策略;maximumPoolSize
线程池中允许的最大线程个数;keepAliveTime
用于设置那些超出核心线程数量的线程的最大等待时间,超过这个时间还没有新 任务的话,超出的线程将被销毁unit
超时时间单位;workQueue
线程队列。用于保存通过execute
方法提交的,等待被执行的任务;threadFactory
线程创建工程,即指定怎样创建线程; public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
handler
拒绝策略。即指定当线程提交的数量超出了maximumPoolSize
后,该使用什么策略处理 超出的线程。代码
class NumberThread implements Runnable{ @Override public void run() { for(int i = 0;i <= 100;i++){ if(i % 2 == 0){ System.out.println(Thread.currentThread().getName() + ": " + i); } } } } class NumberThread1 implements Callable{ @Override public void run() { for(int i = 0;i <= 100;i++){ if(i % 2 != 0){ System.out.println(Thread.currentThread().getName() + ": " + i); } } } } public class ThreadPool { public static void main(String[] args) { //1. 提供指定线程数量的线程池 ExecutorService service = Executors.newFixedThreadPool(10); ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; //设置线程池的属性 // System.out.println(service.getClass()); // service1.setCorePoolSize(15); // service1.setKeepAliveTime(); //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象 service.execute(new NumberThread());//适合适用于Runnable service.submit(new NumberThread());//适合使用于Callable //3.关闭连接池 service.shutdown(); } }