- 并行:指两个或多个事件在同一时刻发生(同时发生);
- 并发:指两个或多个事件在同一个时间段内发生(在多个CPU系统中 可以分配到多个处理器上 并行处理的任务越多、效率越高;单核处理器的计算机不能并行处理多个任务)。
- 进程:内存中一个运行的应用程序,每个进程都有一个独立的内存空间,一个程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位,系统运行一个程序即是一个进程从创建、运行到消亡的过程;
- 线程:进程中的一个执行单位、负责当前进程中程序的执行、一个进程至少有一个线程,一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
区别:
- 进程:有独立的内存空间,进程中的数据存放空间(堆空间 栈空间)是独立的,至少有一个线程;
- 线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。
线程调度:多个线程执行时,线程会被JVM控制以某种方式执行,我们把这种情况称之为线程调度。JVM采用的是抢占式调度,没有采用分时调度,因此可以造成线程执行结果的随机性。
一、创建线程
java使用java.lang.Thread类代表线程,所有的线程对象都必须是Thread类或者其子类的实例,每个线程的作用是完成一个定的任务。Java使用线程执行体来代表这段程序流
线程方式1 :单继承,线程与任务在一起
// 线程的入门
public class DemoThread {
public static void main(String[] args) {
/*
并行:指两个或多个事件在同一时刻发生
并发:指两个或多个事件在同一时间内发生
进程:系统运行的基本单位、任务管理器进程 (有独立的内存空间)
线程:一个进程可以有多个线程、(栈空间独立、堆公用)
*/
myThread tm1 = new myThread();
myThread tm2 = new myThread("旺财");
// 线程开启 交替输出
tm1.start();
tm2.start();
for (int i = 0; i < 100; i++) {
System.out.println("小强:"+i);
}
}
}
// 线程方式1 单继承,线程与任务在一起,
class myThread extends Thread{
// 无参构造
public myThread() {
super();
}
// 有参构造
public myThread(String name) {
super(name);//指定线程名字
}
@Override
public void run() { // 线程任务开始的地方
for (int i = 0; i < 100; i++) {
System.out.println(this.getName()+":"+i);
}
}
}
线程方式2 :声明实现Runnable接口类
步骤:自定义类、实现run方法、创建任务类对象、创建Thread对象-传递任务对象
方式2的好处:避免Java单继承的局限性、解耦合(线程与任务分离、拓展性好)
public class DemoRunnable {
public static void main(String[] args) {
// 3 创建任务类对象
MyTask task = new MyTask();
// 2 使用自定义类 把任务对象当作参数传递 ,分配一个有指定目标的线程对象
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task, "小强哥");
// 0 匿名内部类
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
// 获取执行当前线程任务的名字
String threadName = Thread.currentThread().getName();
// 任务
for (int i = 0; i < 10; i++) {
System.out.println(threadName + " : " + i);
}
}
},"匿名内部类线程");
// 3 开启线程(任务)
thread1.start();
thread2.start();
thread3.start();
}
}
// 1、自定义类,实现Runnable接口 这个类就是任务类
class MyTask implements Runnable {
public MyTask() {}
// 2 实现run方法 run函数中写的是线程要执行的任务代码
@Override
public void run() { // 线程要完成的任务
// 获取执行当前线程任务的名字
Thread thread = Thread.currentThread();
String threadName = thread.getName();
// 任务
for (int i = 0; i < 10; i++) {
System.out.println(threadName + " : " + i);
}
}
}
二、线程状态
1、NEW 新建 线程刚被创建 但未启动。还没有调用start()方法,MyThread t = new Thread() 只有线程对象 没有线程特征 2、Runnable 就绪 线程在Java虚拟机中运行的状态,可能正在运行自己的代码、也可能没有,这取决于操作系统处理器 调用了t.start()方法 3、Block 阻塞状态 当一个线程试图获取一个对象锁 而该对象锁被其他的线程持有,则线程进入Block状态;当线程持有锁时,该线程将变成Runnable状态 4、Waiting 无限等待 一个线程在等待另一个线程执行(唤醒)动作时,该线程进入Waiting状态,进入这个状态后是不能自动唤醒的,必须等另一线程调用notify、notifyAll方法才能唤醒 5、TimedWaiting 计时等待 同Waiting状态,有几个方法有超时参数 调用他们将进入TimedWaiting状态,这一状态一直保持到超时期满或者接到唤醒通知。带有超时参数的常用方法有Thread.sleep、Object.wait 6、Terminated 被种终止 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡
限时等待
public static void main(String[] args) {
// 被锁对象
Object obj = new Object();
// 创建新线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程开启~ 开始获取锁");
synchronized (obj){
System.out.println("进入限时等待");// 进入等待 释放锁对象 程序阻塞,不会往下执行
try {
obj.wait(2000);// 有限等待
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("被唤醒 并获取了锁");
}
}
}).start();
}
waiting & notify (无限等待 唤醒)
public class DemoThreadStateWaitNotify {
public static void main(String[] args) {
// 无限等待
Object obj = new Object();
// 线程A
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("【AAA】线程开启~ 开始获取锁");
synchronized (obj){
System.out.println("【AAA】进入无限等待");// 进入等待 释放锁对象 程序阻塞,不会往下执行
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("【AAA】被唤醒 并获取了锁");
}
System.out.println("【AAA】 完成锁释放");
}
}).start();
// 线程B
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("【BBB】线程开启~ 开始获取锁");
synchronized (obj){
System.out.println("【BBB】占有资源,2s后 唤醒AA");
sleep(); // sleep 2s
obj.notify(); // 唤醒线程AA 不释放锁
System.out.println("【BBB】已经唤醒A 2s后 释放锁");
sleep(); // sleep 2s
}
System.out.println("【BBB】 完成锁释放");
}
private void sleep() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}//main
}//class
三、线程安全
问题引出:金典案例卖票问题。解决方案:同步锁
1-方法锁(详见代码)
// 线程安全案例
public class DemoSellTicket {
public static void main(String[] args) {
SellTicketTask task = new SellTicketTask(); // 执行同一个任务
// 三个线程执行同一个任务
new Thread(task, "窗口1").start(); // 窗口1
new Thread(task, "窗口2").start(); // 窗口2
new Thread(task, "窗口3").start(); // 窗口3
/* 同步锁是谁
1、非静态方法同步锁就是this 一定要保证多个线程中调用方法是同一个对象
2、静态方法同步锁 字节码对象*/
}// main end
}
class SellTicketTask implements Runnable {
static int ticket = 100;
@Override
public void run() {
for (int i=0;i<100;i++) {
//sellT();
sellT2();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// this就是当前任务对象,多个线程都用同一个任务 可以保证多个线程使用的this是同一个锁
// this就是调用方法的对象
public synchronized void sellT() {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + " 正在卖票-> " + ticket);
ticket--;
}
}
// 如果是静态方法 锁 就是当前类型的字节码文件,也就只有一份 SellTicketTask.class
public static synchronized void sellT2(){ //
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + " 正在卖票-> " + ticket);
ticket--;
}
}
}
2-同步代码块(详见代码)
// synchronized 方法二 同步代码块
class SellTicketTask1 implements Runnable {
int ticket = 100;
final Object lockObj = new Object();
@Override
public void run() {
synchronized (lockObj) { // 把操作资源 放入操作代码块 代码互斥访问
for (int i = 0; i < 100; i++) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + " 正在卖票: " + ticket);
ticket--;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}//run
}//class
3-Lock 锁(详见代码)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// 线程安全案例
public class DemoSellTicket2 {
public static void main(String[] args) {
// 同步锁实现
SellTicketTask2 task = new SellTicketTask2();// 执行同一个任务
// 三个线程执行同一个任务
new Thread(task, "窗口1").start(); // 窗口1
new Thread(task, "窗口2").start(); // 窗口2
new Thread(task, "窗口3").start(); // 窗口3
}
}
// Lock 锁
// lock锁机制解决线程锁安全问题
// 1 根据子类 ReentrantLock实例化Lock对象
// 2 保证多个线程使用相同的Lock锁对象
// 3 lock() 方法执行就是开始锁
// 4 unlock() 方法执行就是释放锁
class SellTicketTask2 implements Runnable {
int ticket = 100;
final Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock(); // 上锁
try{
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖票::" + ticket);
ticket--;
}
}finally {
lock.unlock();// 可以保证一定释放锁资源
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}//run
}
四、死锁
两个或两个以上的线程、两个或两个以上的对象锁、存在锁嵌套 --> 会出现死锁情况
public class DemoThreadDeath {
/*线程死锁的条件*/
public static void main(String[] args) {
// 1 多把锁
Object lockObj1 = new Object();
Object lockObj2 = new Object();
// 2 多线程
// 3 锁嵌套
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (lockObj1) {
System.out.println("线程1获取了 obj1");
synchronized (lockObj2) {
System.out.println("线程1获取了 obj2");
}
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (lockObj2) {
System.out.println("线程2获取了 obj2");
synchronized (lockObj1) {
System.out.println("线程2获取了 obj1");
}
}
}
}
}).start();
}
}
- 线程1获取了 obj1
- 线程1获取了 obj2
- 线程1获取了 obj1
- 线程1获取了 obj2
- 线程1获取了 obj1
- 线程2获取了 obj2(卡死在此)
五、线程池
1-Runnable
- 创建线程池:ExecutorService threadPool = Executors.newFixedThreadPool(3);
- 创建任务类:class Student implements Runnable{ run(){} }
- 创建任务对象并提交: threadPool.submit(new Student("张三")); threadPool.submit(new Student("张四"));
- 线程池关闭: threadPool.shutdown();
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class DemoThreadPoolRunnable {
public static void main(String[] args) {
// 1 创建线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3);
// 3 创建任务对象,并提交
threadPool.submit(new Student("张三"));
threadPool.submit(new Student("张四"));
threadPool.submit(new Student("张五"));
threadPool.submit(new Student("张六"));
threadPool.submit(new Student("张七"));
// 4 线程池关闭 【一般不关闭】
threadPool.shutdown();
//threadPool.shutdownNow();
}
}
// 2 创建任务类对象
class Student implements Runnable {
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void run() {
String coach = Thread.currentThread().getName();
System.out.println(coach + " 开始教 " + name + " 游泳");
try {
Thread.sleep(2000);// 教学中
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(coach + " 教 " + name + " 游泳完成!");
}
}
2-Callable
- 创建线程池:ExecutorService threadPool = Executors.newFixedThreadPool(3);
- 创建任务类:class Calculator implements Callable<Integer>{ public Integer call(){} }
- 创建任务对象并提交:
Future<Integer> f = threadPool.submit(new Calculator(100)); Integer result = f.get(); System.out.println("result = " + result);
- 线程池关闭: threadPool.shutdown();
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.Callable;
public class DemoThreadPoolCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1 创建线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3);
// 3 创建任务对象,并提交
Future<Integer> f = threadPool.submit(new Calculator(100));
Integer result = f.get();
System.out.println("result = " + result);
// 4 线程池关闭 【一般不关闭】
threadPool.shutdown();
//threadPool.shutdownNow();
}
}
// 2 创建任务类对象
class Calculator implements Callable<Integer> {
private int n;
public Calculator(int n) {
this.n = n;
}
@Override
public Integer call() throws Exception {
int sum =0;
for (int i = 0; i <= n; i++) {
sum+=i;
}
return sum;
}
}