一、相关概述:
进程(process)
:是程序的一次执行过程,或是正在运行的一个程序。是一个动态
的过程:有它自身的产生、存在和消亡的过程。 进程作为资源分配的单位, 系统在运行时会为每个进程分配不同的内存区域 。
线程(thread)
:进程可进一步细化为线程,是一个程序内部的一条执行路径。
- 若一个进程同一时间
并行
执行多个线程,就是支持多线程的 - 线程作为
调度
和执行
的单位,每个线程拥有独立的运行栈和程序计数器(pc),
线程切换的开销小 。 - 一个进程中的多个线程共享相同的内存单元/内存地址空间。它们从同一
堆
中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。
并行
:多个CPU同时执行多个任务。
并发
:一个CPU(采用时间片)同时执行多个任务。
二、线程的创建和使用:
-
方式一:
继承Thread类
步骤:
- 定义子类继承Thread类。
- 子类中重写Thread类中的run方法。
- 创建Thread子类对象,即创建了线程对象。
- 调用线程对象start方法:启动线程,调用run方法。
package ThreadExer.ExtendsThreadmode; /** 创建两个线程,分别输出1000以内的偶数和1000以内的奇数。 * @author lhang * @create 2019-07-07 21:01 */ /* 测试两个继承Thread类的子线程 */ public class ThreadTest { public static void main(String[] args) { //3) 创建Thread子类对象,即创建了线程对象。 MyThread1 thread1 = new MyThread1(); MyThread2 thread2 = new MyThread2(); //设置线程的名字 thread1.setName("线程thread1"); thread2.setName("线程thread2"); //4) 调用线程对象start方法:启动线程,调用run方法。 thread1.start(); thread2.start(); } } /* 创建Thread线程类的子类,实现输出1000以内的偶数的功能。 */ //1) 定义子类继承Thread类。 class MyThread1 extends Thread { public MyThread1() { } //2) 子类中重写Thread类中的run方法。 public void run() { for (int i = 0; i < 1000; i++) { if (i % 2 == 0) { System.out.println(this.getName() + ":***" + i + "***"); } } } } /* 创建Thread线程类的子类,实现输出1000以内的奇数的功能 */ //1) 定义子类继承Thread类。 class MyThread2 extends Thread { public MyThread2() { } //2) 子类中重写Thread类中的run方法。 public void run() { for (int i = 0; i < 1000; i++) { if (i % 2 != 0) { System.out.println(this.getName() + ":——" + i + "——"); } } } }
-
方式二:
实现Runnable接口
步骤:
- 定义子类,实现Runnable接口。
- 子类中重写Runnable接口中的run方法。
- 通过Thread类含参构造器创建线程对象。
- 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
- 调用Thread类的start方法:开启线程, 调用Runnable子类接口的run方法。
package ThreadExer.ImplentsRunnableMode; /** * 创建两个线程,分别输出1000以内的偶数和1000以内的奇数。 * * @author lhang * @create 2019-07-07 21:12 */ /* 测试两个实现Runnable接口的子线程 */ public class ThreadTest { public static void main(String[] args) { //3) 通过Thread类含参构造器创建线程对象。 MyThread1 myThread1 = new MyThread1(); MyThread2 myThread2 = new MyThread2(); //4) 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。 Thread thread1 = new Thread(myThread1); Thread thread2 = new Thread(myThread2); thread1.setName("线程1"); thread2.setName("线程2"); //5) 调用Thread类的start方法:开启线程, 调用Runnable子类接口的run方法。 thread1.start(); thread2.start(); } } /* 创建实现Runnable接口类,实现输出1000以内的偶数的功能。 */ //1) 定义子类,实现Runnable接口。 class MyThread1 implements Runnable { //2) 子类中重写Runnable接口中的run方法。 @Override public void run() { for (int i = 0; i < 1000; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":**" + i + "**"); } } } } /* 创建实现Runnable接口类,实现输出1000以内的奇数的功能。 */ //1) 定义子类,实现Runnable接口。 class MyThread2 implements Runnable{ //2) 子类中重写Runnable接口中的run方法。 @Override public void run() { for (int i = 0; i < 1000; i++) { if (i % 2 != 0) { System.out.println(Thread.currentThread().getName()+":——" + i + "——"); } } } }
-
方式三:实现Callable接口
步骤
1).创建一个实现Callable的实现类
2).实现call方法,将此线程需要执行的操作声明在call()中
3).创建Callable接口实现类的对象
4).将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
5).将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
6).获取Callable中call方法的返回值
package ThreadExer.ImplementsCallableMode; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * @author lhang * @create 2019-07-08 16:31 */ public class ThreadTest { public static void main(String[] args) { //3.创建Callable接口实现类的对象 Mythread1 mythread1 = new Mythread1(); Mythread2 mythread2 = new Mythread2(); //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象 FutureTask futureTask1 = new FutureTask(mythread1); FutureTask futureTask2 = new FutureTask(mythread2); //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start() Thread thread1 = new Thread(futureTask1); Thread thread2 = new Thread(futureTask2); thread1.setName("线程1"); thread2.setName("线程2"); thread1.start(); thread2.start(); //6.获取Callable中call方法的返回值 //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。 try { Object sum1 = futureTask1.get(); Object sum2 = futureTask2.get(); System.out.println("**" + sum1); System.out.println("——" + sum2); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } /* 功能:创建一个实现Callable接口的类,实现输出1000以内的偶数,并将偶数之和作为返回值。 */ //1.创建一个实现Callable的实现类 class Mythread1 implements Callable { //2.实现call方法,将此线程需要执行的操作声明在call()中 @Override public Object call() throws Exception { int sum = 0; for (int i = 0; i < 1000; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + "**" + i + "**"); sum += i; } } return sum; } } /* 功能:创建一个实现Callable接口的类,实现输出1000以内的奇数,并将奇数之和作为返回值。 */ //1.创建一个实现Callable的实现类 class Mythread2 implements Callable { //2.实现call方法,将此线程需要执行的操作声明在call()中 @Override public Object call() throws Exception { int sum = 0; for (int i = 0; i < 1000; i++) { if (i % 2 != 0) { System.out.println(Thread.currentThread().getName() + "——" + i + "——"); sum += i; } } return sum; } }
-
方式四:使用线程池
package ThreadExer.ThreadPoolMode; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static java.lang.Long.*; public class ThreadTest { public static void main(String[] args) { //1. 提供指定线程数量的线程池 ExecutorService executorService = Executors.newFixedThreadPool(2); ThreadPoolExecutor service = (ThreadPoolExecutor) executorService; //设置线程池的属性 //System.out.println(service.getClass()); //service.setCorePoolSize(15); //service.setKeepAliveTime(Long.MAX_VALUE, TimeUnit.SECONDS); //service.setCorePoolSize(2); //service.setMaximumPoolSize(2); //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象 MyThread1 thread1 = new MyThread1(); MyThread2 thread2 = new MyThread2(); service.execute(thread1);//适合适用于Runnable service.execute(thread2);//适合适用于Runnable //service.submit(Callable callable);//适合使用于Callable //3.关闭连接池 service.shutdown(); } } /* 创建实现Runnable接口类,实现输出1000-2000以内的偶数的功能。 */ class MyThread1 implements Runnable { @Override public void run() { for (int i = 10000; i < 20000; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":**" + i + "**"); } } } } /* 创建实现Runnable接口类,实现输出1000以内的奇数的功能。 */ class MyThread2 implements Runnable{ @Override public void run() { for (int i = 0; i < 10000; i++) { if (i % 2 != 0) { System.out.println(Thread.currentThread().getName()+":——" + i + "——"); } } } }
三、线程的生命周期 :
在线程的一个完整的生命周期中通常要经历如下的五种状态
:
新建
: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态。就绪
: 处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源。运行
: 当就绪的线程被调度并获得CPU资源时,便进入运行状态, run()方法定义了线程的操作和功能。阻塞
: 在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中止自己的执行,进入阻塞状态。死亡
: 线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束 。
四、线程的同步机制的使用
-
synchronized 代码块锁
synchronized (对象){ // 需要被同步的代码; } /* synchronized的锁是什么? - 任意对象都可以作为同步锁。 所有对象都自动含有单一的锁(监视器) 。 - 同步方法的锁:静态方法(类名.class) 、 非静态方法(this) - 同步代码块:自己指定, 很多时候也是指定为this或类名.class 注意: - 必须确保使用同一个资源的多个线程共用一把锁, 这个非常重要, 否则就无法保证共享资源的安全 - 一个线程类中的所有静态方法共用同一把锁(类名.class) , 所有非静态方法共用同一把锁(this) , 同步代码块(指定需谨慎) */
-
synchronized 方法锁
public synchronized void show (String name){ ... } /* 释放锁的操作: - 当前线程的同步方法、同步代码块执行结束。 - 当前线程在同步代码块、同步方法中遇到break、 return终止了该代码块、该方法的继续执行。 - 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception, 导致异常结束。 - 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。 不会释放锁的操作: - 线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行 - 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁(同步监视器)。 注意:应尽量避免使用suspend()和resume()来控制线程 */
-
Lock(锁)
class A{ private final ReentrantLock lock = new ReenTrantLock(); public void m(){ lock.lock(); try{ //保证线程安全的代码; } finally{ lock.unlock(); } } } //如果同步代码有异常,要将unlock()写入finally语句块