目录
一、线程与进程
进程:内存中运行的具有独立内存空间的应用程序
线程:是进程的执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程
二、线程调度
分时调度:所有线程轮流使用CPU使用权,平均分配给每个线程占用CPU的时间
抢占式调度:优先让优先级高的线程使用CPU,如果优先级相同,则会随机选择一个(线程随机性)。使用抢占地调度模式再多个线程之间进行高速切换,对于CPU一个核心而言,某一时刻只能执行一个线程,而CPU再多个线程切换速度相比人眼要快很多,所以看起来是在同一时刻运行,所以多线程程序并不能提高运行速度,但是能够提高程序运行效率,提高CPU使用率。
三、同步与异步
同步:排队执行,效率低但是安全
异步:同时执行,效率高但是不安全
四、并发与并行
并发:两个或多个事件在同一时间段内发生
并行:两个或多个事件在同一时刻同时发生
五、java.lang.Thread类
继承Thread
public class Demo1 {
public static void main(String[] args) {
Thread1 thread1 = new Thread1();
thread1.start();
for(int i=0;i<3;i++){
System.out.println(i);
}
}
}
/**
* run方法就是线程执行的任务方法
*/
class Thread1 extends Thread{
@Override
public void run() {
//不是调用run方法,通过Thread对象调用start()来启动
for(int i=0;i<5;i++){
System.out.println(i);
}
}
}
程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()调用时候被创建。随着调用对象的start方法,另外线程也启动了,这样,整个应用就在多线程下运行。
六、实现java.lang.Runnable接口
/**
* Runnable和Thread相比:
* 通过创建任务,给线程分配实现多线程,更适合多个线程同时执行相同任务的情况
* 避免单继承的局限性
* 任务与线程分离
* 线程池接收Runnable类型任务,不接收Thread类型线程
*/
public class Demo1 {
public static void main(String[] args) {
//实现Runnable,创建任务对象
MyRunnable m = new MyRunnable();
//创建线程分配任务
Thread r = new Thread(m);
r.start();
for(int i=0;i<4;i++){
System.out.println(i);
}
}
}
/**
* 给线程执行的任务
*/
class MyRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println(i);
}
}
}
MyRunnable类通过实现Runnable接口,使得该类有了多线程类的特征。run()方法是多线程程序的一个约定。所有的多线程代码都在run方法里面, 比较以上两个运行结果得知,多线程程序是乱序执行
七、线程类常见方法
sleep(): 强迫一个线程睡眠N毫秒。
isAlive(): 判断一个线程是否存活。
join(): 等待线程终止。
activeCount(): 程序中活跃的线程数。
enumerate(): 枚举程序中的线程。
currentThread(): 得到当前线程。
isDaemon(): 一个线程是否为守护线程。
setDaemon(): 设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程 依赖于主线程结束而结束)
setName(): 为线程设置一个名称。
wait(): 强迫一个线程等待。
notify(): 通知一个线程继续运行。
setPriority(): 设置一个线程的优先级
八、线程安全问题
public class Demo1 {
public static void main(String[] args) {
Runnable r = new Ticket();
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
}
}
class Ticket implements Runnable{
//票数
private int count = 10;
@Override
public void run() {
while(count>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("余票:"+count);
}
}
}
三个线程执行同一个任务出现的安全问题,票数出现负数情况
九、安全问题解决
线程同步synchronized加锁机制
1、同步代码块
public void run() {
synchronized (o) {
while (count > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("余票:" + count);
}
}
}
2、同步方法
@Override
public void run() {
while (true){
boolean flag = sale();
if(!flag){
break;
}
}
}
public synchronized boolean sale(){
while (count > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("余票:" + count);
}
return true;
}
}
3、显示锁Lock
class Ticket implements Runnable {
//票数
private int count = 10;
Lock l = new ReentrantLock();
@Override
public void run() {
while (true) {
if (count > 0) {
l.lock();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("余票:" + count);
}else {
break;
}
l.unlock();
}
}
}
10、带返回值的线程Callable使用步骤
1. 编写类实现Callable接口 , 实现call方法
class XXX implements Callable<T> {
@Override
public <T> call() throws Exception {
return T;
}
}
2. 创建FutureTask对象 , 并传入第一步编写的Callable类对象
FutureTask<Integer> future = new FutureTask<>(callable);
3. 通过Thread,启动线程
new Thread(future).start();
11、线程池
1、缓存线程池
/**
* 缓存线程池.
* (长度无限制)
* 执行流程:
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在,则创建线程 并放入线程池, 然后使用
*/
ExecutorService service = Executors.newCachedThreadPool();
//向线程池中 加入 新的任务
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
})
2、定长线程池
/**
* 缓存线程池.
* (长度无限制)
* 执行流程:
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在,则创建线程 并放入线程池, 然后使用
*/
ExecutorService service = Executors.newCachedThreadPool();
//向线程池中 加入 新的任务
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
})
3、单线程线程池
/**
* 单线程线程池.
* 执行流程:
* 1. 判断线程池 的那个线程 是否空闲
* 2. 空闲则使用
* 4. 不空闲,则等待 池中的单个线程空闲后 使用
*/
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程的名称:"+Thread.currentThread().getName());
}
});
4、周期性任务定长线程池
public static void main(String[] args) {
/**
* 周期任务 定长线程池.
* 执行流程:
* 1. 判断线程池是否存在空闲线程
* 2. 存在则使用
* 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
* 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
*
* 周期性任务执行时:
* 定时执行, 当某个时机触发时, 自动执行某任务 .
*/
ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
/**
* 定时执行
* 参数1. runnable类型的任务
* 参数2. 时长数字
* 参数3. 时长数字的单位
*/
/*service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("俩人相视一笑~ 嘿嘿嘿");
}
},5,TimeUnit.SECONDS);
*/
/**
* 周期执行
* 参数1. runnable类型的任务
* 参数2. 时长数字(延迟执行的时长)
* 参数3. 周期时长(每次执行的间隔时间)
* 参数4. 时长数字的单位
*/
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("俩人相视一笑~ 嘿嘿嘿");
}
},5,2,TimeUnit.SECONDS);
}