一、线程简介
- 程序—进程—线程
- 线程就是独立的执行的路径。即使没有创建线程,后台也会有主线程和GC线程等。
- main()称为主线程。
- 在一个进程中,如果开辟多个线程,线程的运行由调度器安排调度。
- 线程会带来额外的开销,如CPU调度时间,并发控制开销。
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。
二、线程实现(重点)
Thread
- 自定义线程类继承Thread类
- 重写run()方法
- 调用start开启线程
- 注意:线程开启不一定立即执行。
- 不建议使用:避免OOP单继承局限性
/**
* Author:ckvsok
* Date:2021/3/14
**/
package thread;
public class Demo1 extends Thread{
public static void main(String[] args) {
Demo1 demo1 = new Demo1();
//开启线程
demo1.start();
for (int i = 0; i < 1000; i++) {
System.out.println("我是主线程======="+i);
}
}
@Override//重写run方法
public void run() {
for (int i = 0; i < 500; i++) {
System.out.println("我是线程2-------------" +i);
}
}
}
多线程图片下载
/**
* Author:ckvsok
* Date:2021/3/15
**/
package thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class Demo2 extends Thread {
private String url;
private String name;
public Demo2(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url, name);
System.out.println("下载的文件名为:" + name);
}
public static void main(String[] args) {
Demo2 demo2_1 = new Demo2(
"https://img-blog.csdnimg.cn/20210304092856208.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzUzMzAxNjM3,size_16,color_FFFFFF,t_70",
"pic1.png");
Demo2 demo2_2 = new Demo2(
"https://img-blog.csdnimg.cn/20210304092856208.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzUzMzAxNjM3,size_16,color_FFFFFF,t_70",
"pic2.png");
Demo2 demo2_3 = new Demo2(
"https://img-blog.csdnimg.cn/20210304092856208.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzUzMzAxNjM3,size_16,color_FFFFFF,t_70",
"pic3.png");
demo2_1.start();
demo2_2.start();
demo2_3.start();
}
//下载器
class WebDownloader {
public void downloader(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现异常");
}
}
}
}
Runnable
- 定义MyRunnable类实现Runnable接口
- 实现run()方法,编写线程执行体
- 创建线程对象,传入Runnable接口实现类,调用start()方法启动线程
- 推荐使用:避免了单继承局限性,灵活方便,方便同一个对象被多个线程使用
/**
* Author:ckvsok
* Date:2021/3/15
**/
package thread;
public class Demo3 implements Runnable{
public static void main(String[] args) {
//实现Runnable接口,实例化
Demo3 demo3 = new Demo3();
//创建Thread 传入Runnable接口实现类
Thread thread = new Thread(demo3);
//开启线程
thread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("我是主线程======="+i);
}
}
@Override//重写run方法
public void run() {
for (int i = 0; i < 500; i++) {
System.out.println("我是线程2-------------" +i);
}
}
}
多个线程同时操作1个对象
买火车票的例子;出现了线程不安全的问题。
/**
* Author:ckvsok
* Date:2021/3/15
**/
package thread;
//模拟抢票
public class Demo4 implements Runnable {
private int ticketNum = 10;
@Override
public void run() {
while (true){
if (ticketNum<=0){
break;
}
//模拟延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "抢到了倒数第" + ticketNum-- +"张票");
}
}
public static void main(String[] args) {
Demo4 ticket = new Demo4();
//同时抢票
new Thread(ticket,"A").start();
new Thread(ticket,"B").start();
new Thread(ticket,"C").start();
}
}
结果:
B抢到了倒数第10张票
A抢到了倒数第9张票
C抢到了倒数第8张票
A抢到了倒数第7张票
B抢到了倒数第6张票
C抢到了倒数第7张票
A抢到了倒数第4张票
B抢到了倒数第4张票
C抢到了倒数第5张票
C抢到了倒数第3张票
B抢到了倒数第3张票
A抢到了倒数第2张票
C抢到了倒数第1张票
A抢到了倒数第1张票
B抢到了倒数第0张票
模拟龟兔赛跑
/**
* Author:ckvsok
* Date:2021/3/16
**/
package thread;
//模拟龟兔赛跑
public class Demo5 implements Runnable{
private static String winner;
//赛道
@Override
public void run() {
//模拟赛跑
for (int i = 0; i <=100 ; i++) {
//模拟兔子休息
if (Thread.currentThread().getName().equals("兔子") && i/10==1){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果比赛结束了,就不用跑了
if (gameOver(i)){
break;
}
System.out.println(Thread.currentThread().getName() + "跑了"+i+"步");
}
}
public static void main(String[] args) {
//一条赛道,兔子和乌龟一起跑
Demo5 race = new Demo5();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
//判断比赛结束
public Boolean gameOver(int step){
//判断是否存在胜利者
if (winner != null){
return true;
}
if (step == 100){
winner = Thread.currentThread().getName();
System.out.println("最终胜利者是:" + winner);
return true;
}
return false;
}
}
Callable
- 实现,重写call方法,带返回值
- 创建执行服务
- 提交执行
- 返回结果
- 关闭服务
/**
* Author:ckvsok
* Date:2021/3/17
**/
package thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
//下载器 ,使用callable
public class Demo6 implements Callable
{
private String url;
private String name;
public Demo6(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public Boolean call() throws Exception {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载的文件名为:" + name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
Demo6 t1= new Demo6(
"https://img-blog.csdnimg.cn/20210304092856208.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzUzMzAxNjM3,size_16,color_FFFFFF,t_70",
"pic1.png");
Demo6 t2 = new Demo6(
"https://img-blog.csdnimg.cn/20210304092856208.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzUzMzAxNjM3,size_16,color_FFFFFF,t_70",
"pic2.png");
Demo6 t3 = new Demo6(
"https://img-blog.csdnimg.cn/20210304092856208.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L20wXzUzMzAxNjM3,size_16,color_FFFFFF,t_70",
"pic3.png");
//创建执行服务
ExecutorService executorService = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = executorService.submit(t1);
Future<Boolean> r2 = executorService.submit(t2);
Future<Boolean> r3 = executorService.submit(t3);
//返回结果
Boolean rs1 = r1.get();
Boolean rs2 = r1.get();
Boolean rs3 = r1.get();
//关闭服务
}
//下载器
class WebDownloader {
public void downloader(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现异常");
}
}
}
}
静态代理
/**
* Author:ckvsok
* Date:2021/3/17
**/
package thread;
//静态代理 例子:我,婚庆公司,结婚
public class Demo7 {
public static void main(String[] args) {
//被代理对象
Me me = new Me();
//代理对象
Company company = new Company(me);
company.happyMarry();
}
}
//定义接口,结婚
interface Marry{
//结婚方法
void happyMarry();
}
//定义我,实现结婚接口
class Me implements Marry{
@Override
public void happyMarry() {
System.out.println("ck结婚了,超级开心");
}
}
//定义婚庆公司,实现结婚接口,代理类
class Company implements Marry{
//定义被代理对象
private Marry target;
public Company(Marry target) {
this.target = target;
}
@Override
public void happyMarry() {
//结婚前
before();
//被代理对象方法
this.target.happyMarry();
//结婚后
after();
}
private void after() {
System.out.println("给婚庆公司付款");
}
private void before() {
System.out.println("婚庆公司准备婚礼");
}
}
Lamda 表达式
- 实质属于函数式编程的概念
- 函数式接口:一个接口,只含一个抽象方法
- 减少内部类定义
- 实现接口的对象
接口的实现
/**
* Author:ckvsok
* Date:2021/3/17
**/
package thread;
//lambda表达式演变
public class Demo8 {
public static void main(String[] args) {
//1、接口实现类
ILike like1 = new Like1();
like1.like("a");
//2、静态内部类
ILike like2 = new Like2();
like2.like("b");
//3、局部内部类
class Like3 implements ILike{
@Override
public void like(String name) {
System.out.println(name +"->like");
}
}
ILike like3 = new Like3();
like3.like("c");
//4、匿名内部类
ILike like4 = new ILike() {
@Override
public void like(String name) {
System.out.println(name +"->like");
}
};
like4.like("d");
//4、lambda表达式
ILike like5 = (name)->{
System.out.println(name +"->like");
};
like5.like("e");
}
static class Like2 implements ILike{
@Override
public void like(String name) {
System.out.println(name +"->like");
}
}
}
//定义1个函数式接口
interface ILike{
void like(String name);
}
//实现类
class Like1 implements ILike{
@Override
public void like(String name) {
System.out.println(name +"->like");
}
}
三、线程状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9cPpKRJ3-1616546535001)(多线程.assets/image-20210319112722126.png)]
- new 新建尚未启动的状态
- runnable JVM中运行时状态
- blocked 阻塞状态
- waiting 正在等待另一个线程执行特定动作
- timed_waiting 正在等待另一个线程到达指定等待时间
- terminated 已退出
/**
* Author:ckvsok
* Date:2021/3/19
**/
package thread;
//观察线程状态
public class Demo12 {
public static void main(String[] args) throws InterruptedException {
TestStatue testStatue = new TestStatue();
//
Thread thread = new Thread(testStatue);
//启动,运行监听
Thread.State state = thread.getState();
System.out.println("启动状态:" + state);
thread.start();
state = thread.getState();
System.out.println("运行状态 : " + state);
//循环监听
while (state != Thread.State.TERMINATED){
Thread.sleep(100);
state = thread.getState();
System.out.println("状态 :" + state);
}
}
}
class TestStatue implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("结束");
}
}
四、线程方法
- setPriority(int newPriority) 设置优先级
- static void sleep
- void join()
- static void yield()
- void interrupt
- boolean is Alive()
停止线程
-
不推荐使用JDK提供的stop和destroy方法
-
推荐线程自己停下来
-
建议使用一个标志位进行终止变量
当flag = false,则终止线程运行。
/**
* Author:ckvsok
* Date:2021/3/19
**/
package thread;
//测试线程stop
//1.建议线程正常停止 ---利用次数,不建议死循环
//2.建议使用标志位 --- 设置一个标志位
//3.不要使用stop或者destroy过时的方法
public class Demo10 implements Runnable{
// 1.设置1个标志位
private boolean flag = true;
public static void main(String[] args) {
Demo10 testStopTread = new Demo10();
new Thread(testStopTread).start();
for (int i = 0; i < 1000; i++) {
if (i == 900){
testStopTread.stop();
System.out.println("测试线程结束了------------");
}
System.out.println("main线程运行 :" + i);
}
}
@Override
public void run() {
int i = 0;
while (flag){
System.out.println("测试线程运行中:" + i++);
}
}
//2.设置1个公开的方法停止线程,切换标志位
public void stop(){
flag = false;
}
}
线程休眠
- sleep指定当面线程阻塞的毫秒数
- sleep存在InterrutedException
- sleep时间到达后线程进入就绪状态
- sleep可以模拟网络延时,倒计时等
- 每个对象都有一个锁,sleep不会释放锁
/**
* Author:ckvsok
* Date:2021/3/19
**/
package thread;
import java.text.SimpleDateFormat;
import java.util.Date;
//模拟倒计时
public class Demo11{
public static void main(String[] args) {
countDown();
}
public static void countDown(){
int i = 10;
while (i>=0){
try {
Thread.sleep(1000);
Date startDate = new Date(System.currentTimeMillis());//获取当前系统时间
System.out.println("倒计时:" + i -- +" 当前时间:"+ new SimpleDateFormat("HH:mm:ss").format(startDate) );
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
线程礼让
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 将线程从运行状态转化为就绪状态
- 让CPU重新调度,礼让不一定成功!看CPU心情
线程join
- 插队,强制执行
- 待次线程执行完成后,再执行其他线程,其他线程进入阻塞状态
线程优先级
- 线程优先级 1- 10
- 优先级设置建议在start方法前
- 优先级只是获得调度的概率低
- setPriority(int newPriority)
守护线程deamon
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 守护线程:日志 ,GC等
- setDaemon();默认是false
五、线程同步(重点)
多个线程操作同一个资源,使用队列和锁解决线程完全问题。
例子1:不安全的买票
/**
* Author:ckvsok
* Date:2021/3/19
**/
package thread;
public class UnsafeBuyTicket {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(ticket,"A").start();
new Thread(ticket,"B").start();
new Thread(ticket,"C").start();
}
}
class Ticket implements Runnable{
private int ticketNub = 10;
private boolean flag = true; //外部停止条件
//买票
@Override
public void run() {
while (flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void buy() throws InterruptedException {
//判断是否有票
if (ticketNub <= 0){
flag = false;
return;
}
//模拟延时
Thread.sleep(100);
//买到了就减一张
System.out.println(Thread.currentThread().getName() + "买到" + ticketNub --);
}
}
例子2:银行取钱
/**
* Author:ckvsok
* Date:2021/3/19
**/
package thread;
// 两人从同一个银行账户取钱
public class UnsafeBank {
public static void main(String[] args) throws InterruptedException {
Account account = new Account(1000);
Thread a = new Thread(account, "A");
Thread b = new Thread(account, "B");
a.start();
b.start();
}
}
//银行账户
class Account implements Runnable {
//账户金额
private int money = 100000;
//账户需要减少的
private int num;
public Account(int num){
this.num = num;
}
@Override
public void run() {
//取钱
try {
takeMoney(num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void takeMoney(int num) throws InterruptedException {
while (this.money>num){
this.money = this.money - num;
System.out.println(Thread.currentThread().getName()+"取钱"+num+",账户还剩:"+(money));
}
}
}
例子3:不安全的买票
/**
* Author:ckvsok
* Date:2021/3/19
**/
package thread;
import java.util.ArrayList;
//ArrayList集合是不安全的, 多线程同时操作一个集合时,会重复操作造成覆盖。
public class UnsafeList {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
System.out.println("list集合含有:" + list.size()+"元素");
}
}
synchronized
同步方法:synchronized
同步块:synchronized(Object){}
以上都是锁当前对象(this)
死锁现象
多个线程都持有对象的资源,形成僵持。
/**
* Author:ckvsok
* Date:2021/3/19
**/
package thread;
public class TestDeadLock {
public static void main(String[] args) {
Makeup girl1 = new Makeup(0, "女朋友1号");
Makeup girl2 = new Makeup(1, "女朋友2号");
girl1.start();
girl2.start();
}
}
class Makeup extends Thread{
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;
String girlName;
Makeup(int choice,String girlName){
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
//化妆
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void makeup() throws InterruptedException {
if (choice == 0){
//获得口红的锁
synchronized(lipstick){
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(1000);
//1s后,需要获得镜子的锁
synchronized (mirror){
System.out.println(this.girlName+"获得镜子的锁");
}
}
}else if (choice == 1){
synchronized(mirror){
System.out.println(this.girlName+"获得镜子的锁");
Thread.sleep(1000);
synchronized (lipstick){
System.out.println(this.girlName+"获得口红的锁");
}
}
}
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
解除死锁,可以一件一件的去做,不要嵌套锁
Lock锁
JDK5.0开始,Lock对象。java.util.concurrent.locks.Lock
ReentrantLock类
实现了Lock,可重入锁。
/**
* Author:ckvsok
* Date:2021/3/22
**/
package thread;
import java.util.concurrent.locks.ReentrantLock;
//测试Lock锁
public class TestLock implements Runnable {
public static void main(String[] args) {
TestLock testLock = new TestLock();
new Thread(testLock).start();
new Thread(testLock).start();
new Thread(testLock).start();
}
int ticketNum = 10;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
//加锁
lock.lock();
while (true) {
if (ticketNum > 0) {
Thread.sleep(1000);
System.out.println(ticketNum--);
} else {
break;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
}
}
Lock是显式锁
Lock只有代码锁,相比synchronize性能更好点
六、线程协作
生产者消费者
wait() : 表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁;
notify():唤醒1个处于等待状态的线程;
notifyAll():
以上方法必须在同步代码块中使用,否则有异常。
实现的2种方法:
生产者 --> 缓冲区 —> 消费者
/**
* Author:ckvsok
* Date:2021/3/22
**/
package thread;
//测试:生产者消费者模型 利用缓冲区解决
public class TestPC {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
Consumer consumer = new Consumer(synContainer);
Productor productor = new Productor(synContainer);
productor.start();
consumer.start();
}
}
//消费者
class Consumer extends Thread {
private SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
Chicken chicken = container.pop();
System.out.println("消费----产品id: " + chicken.id);
}
}
}
//生产者
class Productor extends Thread {
private SynContainer container;
public Productor(SynContainer container) {
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("生产----产品id:" + i);
}
}
}
//缓存区
class SynContainer{
private static Chicken[] chickens = new Chicken[10];
private static int count = 0;
public Chicken[] getChickens() {
return chickens;
}
public void setChickens(Chicken[] chickens) {
this.chickens = chickens;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
//生产者生产产品
public synchronized void push(Chicken chicken) {
//如果容器满了,就等待消费者消费
if (count == 10) {
//通知消费者消费,生产者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果容器没有满,就生产产品
chickens[count] = chicken;
count++;
//通知消费者消费
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop() {
//如果容器空了,就等待生产者生产
if (count == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count --;
Chicken chicken = chickens[count];
this.notifyAll();
return chicken;
//通知生产者生产产品
}
}
//产品
class Chicken {
int id;
public Chicken(int id) {
this.id = id;
}
}
信号灯模式:
/**
* Author:ckvsok
* Date:2021/3/23
**/
package thread;
//测试:生产者消费者模型2 信号灯法
public class TestPC2 {
public static void main(String[] args) {
Chicken2 chicken2 = new Chicken2();
Consumer2 consumer = new Consumer2(chicken2);
Productor2 productor = new Productor2(chicken2);
productor.start();
consumer.start();
}
}
//消费者
class Consumer2 extends Thread {
private Chicken2 chicken;
public Consumer2(Chicken2 chicken) {
this.chicken = chicken;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
chicken.pop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//生产者
class Productor2 extends Thread {
private Chicken2 chicken;
public Productor2(Chicken2 chicken) {
this.chicken = chicken;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
chicken.push();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//产品
class Chicken2 {
boolean flag = true;
public Chicken2() {
}
//生产
public synchronized void push() throws InterruptedException {
if (!flag) {
this.wait();
}
flag = !flag;
System.out.println("正在生产,请等待");
Thread.sleep(1000);
this.notifyAll();
}
public synchronized void pop() throws InterruptedException {
if (flag) {
this.wait();
}
flag = !flag;
System.out.println("正在消费,请等待");
Thread.sleep(1000);
this.notifyAll();
}
}
线程池
提高响应速度,降低资源消耗,
便于线程管理
corePoolSize:核心池的大小
maxinumPoolSize:最大线程数
keepAliveTime 线程没有任务时最多保持多长时间后会终止
ExecutorService :线程池接口
void execute(Runnable command)
Future submit(Callable task)
void shutdown()
Executors : 工具类、线程池的工厂类