程序、进程和线程
- 程序:指令和数据的有序集合,一个静态的概念。ian
- 进程:执行程序的一次执行过程,一个动态的概念。是系统资源分配的单位。
- 线程:一个进程有若干个线程。是CPU调度和执行的单位。
线程创建三种方式
- 继承 Thread 类
- 实现 Runnable 接口
- 实现Callable 接口(了解)
推荐使用第二种,实现Runnable接口,灵活方便
继承Thread类
package CreateThread.demo1;
public class ThreadDemo1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我是线程" + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
实现Runnable接口
package CreateThread.demo2;
public class ThreadDemo2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我是线程" + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
实现Callable接口
TestCallable thread = new TestCallable();//实现了Callable接口的自定义类
ExecutorService ser = Executors.newFixedThreadPool(1);//创建执行服务
Future<Boolean> result = ser.submit(thread);//执行提交,Boolean这里是泛型
boolean r = result.get();//获取结果
ser.shutdownNow();//关闭服务
线程使用例子
多线程输出
package CreateThread.demo1;
// 线程不一定马上执行,由CPU调度决定
public class Main {
public static void main(String[] args) {
ThreadDemo1 threadDemo1 = new ThreadDemo1();
threadDemo1.start();
for (int i = 1; i < 20; i++) {
System.out.println("我是主线程" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
多线程下载图片
线程类
package UserThread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class ThreadDownload extends Thread {
private String url;
private String name;
public ThreadDownload(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public void run() {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
System.out.println("下载文件" + name);
} catch (IOException e) {
e.printStackTrace();
System.out.println("下载文件" + name + "异常");
}
}
}
main
package UserThread;
//多线程下载图片
public class Main {
public static void main(String[] args) {
ThreadDownload threadDownload1 = new ThreadDownload("https://img-blog.csdnimg.cn/img_convert/1f20816e15694cf3b56262c1f87e2a14.png","1.jpg");
ThreadDownload threadDownload2 = new ThreadDownload("https://imgconvert.csdnimg.cn/aHR0cHM6Ly9hdmF0YXIuY3Nkbi5uZXQvNy83L0IvMV9yYWxmX2h4MTYzY29tLmpwZw","2.jpg");
ThreadDownload threadDownload3 = new ThreadDownload("https://profile-avatar.csdnimg.cn/b2653aef86c741d6a93d3606ba678d58_weixin_43996464.jpg!1","3.jpg");
threadDownload1.start();
threadDownload2.start();
threadDownload3.start();
}
}
龟兔赛跑
线程类
package UserThread.demo3;
import UserThread.demo2.TickMain;
public class ThreadDemo implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
// 兔子会每步休息一下
if (Thread.currentThread().getName().equals("兔兔") && i != 1) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (gameOver(i)) {
break;
}
System.out.println(Thread.currentThread().getName() + "跑了" + i + "步");
}
}
private boolean gameOver(int step) {
// 已经有胜利者
if (RaceMain.winner != null) {
return true;
}
// 抵达终点
if (step >= 10) {
RaceMain.winner = Thread.currentThread().getName();
System.out.println(Thread.currentThread().getName() + "到达终点!");
return true;
}
// 比赛继续
return false;
}
}
RaceMain
package UserThread.demo3;
public class RaceMain {
public static String winner;
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
new Thread(threadDemo, "兔兔").start();
new Thread(threadDemo, "龟龟").start();
}
}
线程并发问题
多线程抢票
多线程抢票,并发问题,会出现同一张票多个人抢到
线程类
package UserThread.demo2;
public class ThreadDemo implements Runnable {
@Override
public void run() {
while (true) {
if (TickMain.ticks <= 0) {
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "抢到票" + TickMain.ticks--);
}
}
}
TickMain
package UserThread.demo2;
//多线程抢票,并发问题,会出现同一张票多个人抢到
public class TickMain {
public static int ticks = 10;
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
new Thread(threadDemo,"小明").start();
new Thread(threadDemo,"小红").start();
new Thread(threadDemo,"黄牛").start();
}
}
线程停止
- 不使用不推荐的停止方法,例如官方的stop、destroy()
- 建议使用标志位来控制线程停止
package UserThread.demo4;
// 线程停止
// 不使用不推荐的停止方法,例如官方的stop、destroy()
// 建议使用标志位来控制线程停止
public class StopThread implements Runnable {
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag) {
System.out.println("线程输出" + i++);
}
}
public void stop() {
flag = false;
}
public static void main(String[] args) {
StopThread stopThread = new StopThread();
new Thread(stopThread).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main" + i);
if (i == 900) {
stopThread.stop();
System.out.println("线程停止-------------");
}
}
}
}
线程相关方法
方法 | 说明 |
---|---|
setPriority(int newPriority) | 更改线程优先级 |
static void sleep(long milis) | 指定毫秒线程休眠(运行状态到阻塞状态) |
void join() | 等待线程终止 |
static void yield() | 暂停当前正在执行的线程对象,并执行其他线程(运行状态到就绪状态) |
void interrupt() | 中断线程(别用这个方式) |
boolean isAlive() | 测试线程是否处于活动状态 |
线程五个状态
- NEW:尚未启动状态。
- RUNNABLE:在Java虚拟机中执行的线程处于此状态。
- BLOCKED:被阻塞等待监视器锁定的线程处于此状态。
- WITING:正在等待另一个线程执行特定动作的线程处于此状态。
- TIME_WITING:正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
- TERMINATED:已退出的线程。
通过getState()就可以获取线程的状态。
守护线程
- 线程分为
守护线程
和用户线程
。 - 虚拟机必须确保用户线程执行完毕。
- 虚拟机不用等待守护线程执行完毕。
- 如:后台记录操作日志、监控内存、垃圾回收等待…
thread.setDaemon(true) // 设置为守护线程
线程同步
线程同步就是给共用资源加锁,线程需要获得锁才能进行操作,否则会阻塞。
关键字 synchronized,两种用法:synchronized 方法 和 synchronized 块。
//同步方法
pubilc synchronized void method(int args){
...
}
//同步块
synchronized(obj){
...
}
1.同步块 obj 一般都是共享资源。
2.同步方法默认锁 this。
3.若将一个大方法申明为同步,会降低效率。
生产消费者问题
管程法
package UserThread.GuanCheng;
// 多线程-管程法处理线程通信
public class GuanCheng {
public static void main(String[] args) {
BufferChicken bufferChicken = new BufferChicken();
new Productor(bufferChicken).start();
new Consumer(bufferChicken).start();
}
}
// 生产者
class Productor extends Thread {
private BufferChicken buffer;
public Productor(BufferChicken buffer) {
this.buffer = buffer;
}
@Override
public void run() {
// 生产
for (int i = 1; i < 100; i++) {
buffer.push(i);
}
}
}
// 消费者
class Consumer extends Thread {
private BufferChicken buffer;
public Consumer(BufferChicken buffer) {
this.buffer = buffer;
}
@Override
public void run() {
// 生产
for (int i = 1; i < 100; i++) {
buffer.pop();
}
}
}
// 产品
class Chicken {
private int id;
public Chicken(int id) {
this.id = id;
}
public int getId() {
return id;
}
}
// 缓冲区
class BufferChicken {
private Chicken[] chicken = new Chicken[10];
private int count = 0;
// 生成
public synchronized void push(int id) {
// 满了,等待消费
if (count >= chicken.length) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 生产
Chicken chicken = new Chicken(id);
System.out.println("生产第" + id + "只鸡!");
this.chicken[count++] = chicken;
this.notifyAll();
}
//消费
public synchronized void pop() {
//判断是否存在产品,若不存在则等待生产
if (count <= 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//消费
System.out.println("消费第" + this.chicken[--count].getId() + "只鸡~");
this.notifyAll();
}
}
信号灯法
package UserThread.SignalLight;
// 信号灯法
public class SignalLight {
public static void main(String[] args) {
TV tv = new TV();
new Productor(tv).start();
new Comsumer(tv).start();
}
}
//生产者
class Productor extends Thread {
private TV tv;
public Productor(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i % 2 == 0) {
tv.play("斗破苍穹");
} else {
tv.play("斗罗大陆");
}
}
}
}
//消费者
class Comsumer extends Thread {
private TV tv;
public Comsumer(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
//产品
class TV {
private String name;
// true的时候表演,false的时候观看
private boolean flag = true;
//表演
public synchronized void play(String name) {
if (!flag) {
try {
System.out.println("表演阻塞拉!");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了:" + name);
this.name = name;
this.flag = false;
this.notifyAll();
}
//观看
public synchronized void watch() {
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众观看了:" + this.name);
this.flag = true;
this.notifyAll();
}
}
死锁
多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致多个线程都在等待对方释放资源,都停止执行的情况就是死锁。
某一个同步块同时拥有两个以上对象的锁
时,就可能发生死锁
的问题。
产生死锁的四个必要条件
- 互斥条件:一个资源每次只能被一个进程使用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
- 循环等待条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
上面列出了4个必要条件,只需破坏任意一个条件即可避免死锁。
Lock 与 synchronized 的区别
- Lock 是显式锁(手动开关锁),synchronized是隐式锁,出了作用域自动释放。、
- Lock 只有代码块锁, synchronized 有代码块锁和方法锁
- 使用 Lock 锁,JVM 将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)。
- 优先使用顺序:lock > 同步代码块 > 同步方法
使用lock锁例子:
private final ReentrantlLock lock = new ReentrantlLock();
public void method(){
lock.lock();
...
lock.unlock();
}
线程池
什么是线程池
- 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大
- 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁,实现重复利用,类似生活中的公共交通工具
- 好处:
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 便于线程管理(…)
- corePoolSize:核心池的大小
- maximumPoolSize:最大线程数
- keepAliveTime:线程没有任务时最多保持多长时间后会终止
使用线程池
- ExecutorService:线程池接口。常见子类ThreadPoolExecutor
- void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
- Future submit(Callable task):执行任务,有返回值,一般又来执行Callable
- void shutdown():关闭连接池
- Executors:工具类、线程池的工厂类,用于创建并返回不用类型的线程池
七大参数
- corePoolSize:线程池核心线程大小
- maximumPoolSize:线程池最大线程数量
- keepAliveTime:空闲线程存活时间
- unit:空闲线程存活时间单位
- workQueue:工作队列 (1.ArrayBlocking 2.LinkedBlockingQueue 3.SynchronizedQueue 4.PriorityBlockingQueue)
- ThreadFactory:线程工厂
- Handler 拒绝策略