多线程
多线程的核心概念
1.线程创建
1.1继承Thread类
/**
*继承Thread类,重写run()方法,调用start开启线程
*总结:线程开启不一定立即执行,由cpu调度执行
*/
public class Demo01 extends Thread{
@Override
public void run() {
//run()方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码"+i);
}
}
public static void main(String[] args) {
//创建一个线程对象
Demo01 demo01 = new Demo01();
//调用start()方法,启动线程。
demo01.start();
//main方法主线程
for (int i = 0; i < 20; i++) {
System.out.println("我在学习多线程"+i);
}
}
}
练习Thread,实现多线程同步下图片
public class TestThread01 extends Thread{
private String url;//保存图片地址
private String name;//保存的文件名
public TestThread01(String url,String name){
this.name=name;
this.url=url;
}
@Override
public void run() {
WebDownLoader webDownLoader = new WebDownLoader();
webDownLoader.downloader(url,name);
System.out.println("下载了文件名为:"+name);
}
public static void main(String[] args) {
TestThread01 t1 = new TestThread01("https://img-blog.csdnimg.cn/514f6b74cde8493d8f995cf8b270c162.gif#pic_center","1.gif");
TestThread01 t2 = new TestThread01("https://img-blog.csdnimg.cn/img_convert/94e1ee8fac0e29b629b83e795e542743.png","2.png");
TestThread01 t3 = new TestThread01("https://img-blog.csdnimg.cn/20200318135358624.png","3.png");
t1.start();
t2.start();
t3.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方法出现问题");
}
}
}
1.2实现Runnable接口
package com.Thread.Thread01.Demo01;
/**
* 实现runnable接口,重写run()方法,线程需要丢入runnable接口,调用start方法。
*/
public class TestThread02 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码"+i);
}
}
public static void main(String[] args) {
//创建一个Runnable接口对象
TestThread02 t1= new TestThread02();
//调用start()方法,启动线程。
new Thread(t1).start();
//main方法主线程
for (int i = 0; i < 20; i++) {
System.out.println("我在学习多线程"+i);
}
}
}
上面的图片下载用runnable方法只需多创建一个Thread对象:new Thread(t1).start();
1.3小结
小试牛刀
package com.Thread.Thread01.Demo01;
/**
* 多线程同时操作一个对象
* 买火车票的例子
* 发现问题:多个线程同时操作同一个资源的情况下,线程不安全,数据紊乱。
*/
public class TestThread4 implements Runnable{
//票数
private int ticketNums=10;
@Override
public void run() {
while (true){
if (ticketNums<=0){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--"+"拿到了第"+ticketNums--+"票");
}
}
public static void main(String[] args) {
TestThread4 t1 = new TestThread4();
new Thread(t1,"小明").start();
new Thread(t1,"晓红").start();
new Thread(t1,"黄牛").start();
}
}
2.静态代理
package com.Thread.Demo03;
/**
* 静态代理模式总结:
* 真实对象和代理对象都要实现统一个接口
* 代理对象必须代理真实角色
* 好处:
* 代理对象可以做很多真实对象做不了的事
* 真实对象可以做自己的事情
*/
public class StaticProxy {
public static void main(String[] args) {
new Thread( ()-> System.out.println("我爱你")).start();
//和Runnable方法进行对比
new WeddingCompany(new you()).HappyMarry();
// WeddingCompany WeddingCom=new WeddingCompany(new you());
// WeddingCom.HappyMarry();
}
}
interface Merry{
void HappyMarry();
}
class you implements Merry{
@Override
public void HappyMarry() {
System.out.println("我要结婚了,超开心");
}
}
class WeddingCompany implements Merry{
private Merry target;
public WeddingCompany(Merry target) {
this.target = target;
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry();
after();
}
private void before(){
System.out.println("结婚之前,布置现场");
}
private void after(){
System.out.println("结婚之后,收尾款");
}
}
3.Lambda表达式
任何一个接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口:
pubilc interface Runnable(){
public abstract void run();
}
对于函数式接口,我们可以通过lambda表达式来创建接口的对象
package com.Thread01.Demo01;
/**
* 推导lambda表达式
*/
public class TestLambda {
//3.静态内部类
static class I1 implements like{
@Override
public void lambda() {
System.out.println("I like java2");
}
}
public static void main(String[] args) {
like like = new I();
like.lambda();
like = new I1();
like.lambda();
//4.局部内部类
class I2 implements like{
@Override
public void lambda() {
System.out.println("I like java3");
}
}
like =new I2();
like.lambda();
//5.匿名内部类:没有类的名称,必须借助接口或者父类
like=new like() {
@Override
public void lambda() {
System.out.println("I like java4");
}
};
like.lambda();
//6.用lambda简化
like=()->{
System.out.println("I like java5");
};
like.lambda();
//7.简化花括号:
like=()-> System.out.println("I like java5");
//总结:
//1.lambda表达式只能有一行的情况下才能简化为一行,如果有多行,那么就可以用代码块包裹
//2.多个参数也可以去掉参数类型,要去掉全部去掉,必须加上括号,一个参数时括号也可以去掉。
}
}
//1.定义一个函数式接口
interface like{
void lambda();
}
//2.实现类
class I implements like{
@Override
public void lambda() {
System.out.println("I like java1");
}
}
4.线程代理
1.线程停止
package com.Thread01.Demo01;
/**
*测试stop
* 1.建议线程正常停止---利用次数,不建议死循环
* 2.建议使用标志位----设置一个标志位
* 3.不要使用stop或者destroy等过时活着jdk不建议使用的方法
*/
public class TestStop implements Runnable{
//1.设置一个标志位
private boolean flag=true;
@Override
public void run() {
int i=0;
while (flag){
System.out.println("run-----Thread"+i++);
}
}
//2.设置一个公开的方法停止线程,转换标志位
public void stop(){
this.flag=false;
}
public static void main(String[] args) {
TestStop testStop=new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
if (i==900){
//调用stop方法切换标志位,让线程停止
testStop.stop();
System.out.println("线程该停止了");
}
}
}
}
2.线程睡眠-sleep
package com.Thread.Demo04;
/**
* 模拟网络延时:放大问题的发生性
*/
public class TestSleep implements Runnable{
//票数
private int ticketNums=10;
@Override
public void run() {
while (true){
if (ticketNums<=0){
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"票");
}
}
public static void main(String[] args) {
TestSleep thread05 = new TestSleep();
new Thread(thread05,"小明").start();
new Thread(thread05,"老师").start();
new Thread(thread05,"黄牛党").start();
}
}
package com.Thread01.Demo01;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* <h5>描述:</h5>
*/
public class TestSleep {
public static void main(String[] args) {
//倒计时
try {
TenDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
//打印当前的系统时间
Date startTime=new Date(System.currentTimeMillis());
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime= new Date(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//模拟倒计时
public static void TenDown() throws InterruptedException {
int nums=10;
while (true){
Thread.sleep(1000);
System.out.println(nums--);
if (nums<=0){
break;
}
}
}
}
3.线程礼让Yield
-
线程礼让,让当前正在执行的线程停止,但不阻塞
-
将线程从运行状态转为就绪状态
-
让cpu重新调度,礼让不一定成功!看cpu心情。
Thread.yield();
4.Join
-
join合并线程,待此线程执行完成之后,再执行其他线程,其他线程阻塞
-
可以想象成插队
package com.Thread01.Demo01; /** * 测试join方法,可以想象成插队 */ public class TestJoin implements Runnable{ @Override public void run() { for (int i = 0; i < 300; i++) { System.out.println("线程vip来了"+i); } } public static void main(String[] args) throws InterruptedException { TestJoin testJoin=new TestJoin(); Thread thread = new Thread(testJoin); thread.start(); for (int i = 0; i < 100; i++) { if (i==20){ thread.join();//插队 } System.out.println("main"+i); } } }
5.观测线程状态
Thread.State
-
线程状态。线程可以处于以下状态之一:
-
NEW 尚未启动的线程处于此状态。
-
RUNNABLE
在Java虚拟机中执行的线程处于此状态。
-
BLOCKED
被阻塞等待监视器锁定的线程处于此状态。
-
WAITING
正在等待另一个线程执行特定动作的线程处于此状态。
-
TIME_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
-
TERMINATED
已退出的线程处于此状态。
一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。
-
package com.Thread01.Demo01;
/**
* 测试state
*/
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
for (int i = 0; i < 5; i++) {
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("嘤嘤嘤嘤嘤");
});
//观察状态
Thread.State state=thread.getState();
System.out.println(state);
//观察启动后
thread.start();//启动线程
state=thread.getState();
System.out.println(state);//run
//观察状态
while (state!=Thread.State.TERMINATED){//只要线程不终止,就一直输出状态
Thread.sleep(100);
state=thread.getState();//更新线程状态
System.out.println(state);//输出状态
}
}
}
6.线程优先级
package com.Thread01.Demo01;
/**
*测试线程优先级
*/
public class TestPriority{
public static void main(String[] args) {
//主线程默认优先级
System.out.println(Thread.currentThread().getName()+"---"+Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread t1=new Thread(myPriority);
Thread t2=new Thread(myPriority);
Thread t3=new Thread(myPriority);
Thread t4=new Thread(myPriority);
//设置优先级,在启动
t1.start();
t2.setPriority(2);
t2.start();
t3.setPriority(5);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY=10
t4.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"---"+Thread.currentThread().getPriority());
}
}
7.守护(daemon)线程
-
线程分为用户线程和守护线程
-
虚拟机必须确保用户线程执行完毕
-
虚拟机不用等待守护线程执行完毕
-
如:后台记录操作日志,监控内存,垃圾回收等待...
人生不过三万天
package com.Thread01.Demo01;
/**
* 测试守护线程
* 妈妈守护你
*/
public class TestDaemon {
public static void main(String[] args) {
Mum mum = new Mum();
You you = new You();
Thread thread = new Thread(mum);
thread.setDaemon(true);//默认是false表示用户线程,正常的线程都是用户线程。。。
thread.start();//mum守护线程启动
new Thread(you).start();//you 用户线程启动。。。
}
}
//Mum
class Mum implements Runnable{
@Override
public void run() {
while (true){
System.out.println("mum一直陪在你身边");
}
}
}
//you
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("你这一生都很快乐");
}
System.out.println("say GoodBye to world");
}
}
5.线程同步机制
-
多个线程操作同一个资源
-
并发:同一个对象被多个线程同时操作
-
同步方法
public class TestThread05 implements Runnable{
//票数
private int ticketNums=10;
@Override
public synchronized void run() {
while (true){
if (ticketNums<=0){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"票");
}
}
public static void main(String[] args) {
TestThread05 thread05 = new TestThread05();
new Thread(thread05,"小明").start();
new Thread(thread05,"老师").start();
new Thread(thread05,"黄牛党").start();
}
}
-
锁的对象是变化的对象,需要增删改查的对象
package com.Thread01.Demo01;
import java.util.ArrayList;
import java.util.List;
/**
* 测试同步块
*/
public class UnsafeList {
public static void main(String[] args) {
List<String> list=new ArrayList<String>();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
synchronized (list){
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
5.1死锁
-
多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形,某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题。
5.2管程法
package com.Thread01.Demo01;
/**
* 测试:生产者消费者模型-->利用缓冲区解决:管程法
* 生产者,消费者,产品,缓冲区
*/
public class TestPc {
public static void main(String[] args) {
SynContainer container=new SynContainer();
new Productor(container).start();
new Consumer(container).start();
}
}
//生产者
class Productor extends Thread{
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("生产了"+i+"只鸡");
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container){
this.container=container;
}
//消费
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了"+container.pop().id+"只鸡");
}
}
}
//产品
class Chicken{
int id;//产品编号
public Chicken(int id) {
this.id = id;
}
}
//缓冲区
class SynContainer{
//需要一个容器大小
Chicken[] chickens=new Chicken[10];
//容器计数器
int count=0;
//生产者放入产品
public synchronized void push(Chicken chicken){
//如果容器满了,需要等待消费者消费
if (count==chickens.length){
//通知消费者消费,生产者等待
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;
}
}
5.3信号灯法
package com.Thread01.Demo02;
/**
*测试生产者消费者问题2:信号灯法,标志位解决
*/
public class TestPc2 {
public static void main(String[] args) {
TV tv=new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}
//生产者--》演员
class Player extends Thread{
TV tv=new TV();
public Player(TV tv){
this.tv=tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i%2==0){
this.tv.play("斗破苍穹");
}else{
this.tv.play("斗罗大陆");
}
}
}
}
//消费者--》观众
class Watcher extends Thread{
TV tv=new TV();
public Watcher (TV tv){
this.tv=tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
//产品--》节目
class TV{
//演员表演,观众等待
//观众观看,演员等待
String voice;//表演的节目
boolean flag=true;
//表演
public synchronized void play(String voice){
if (!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("冰冰表演了"+voice);
//通知观众观看
this.notifyAll();//通知唤醒
this.voice=voice;
this.flag=!this.flag;
}
//观看
public synchronized void watch(){
if (flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("我观看了"+voice);
//通知演员表演
this.notifyAll();
this.flag=!this.flag;
}
}
5.4线程池
目录
package com.Thread01.Demo02;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 测试线程
*/
public class TestPool {
public static void main(String[] args) {
//创建服务,创建线程池
//newFixedThreadPool 参数为:线程池的大小
ExecutorService service= Executors.newFixedThreadPool(10);
//执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
6.总结
package com.Thread01.Demo02;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 回顾总结线程的创建
*/
public class ThreadNew {
public static void main(String[] args) {
new MyThread1().start();
new Thread(new MyThread2()).start();
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
new Thread(futureTask).start();
try {
Integer integer = futureTask.get();
System.out.println(integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
//1.继承thread类
class MyThread1 extends Thread{
@Override
public void run() {
System.out.println("MyThread1");
}
}
//2.实现Runnable接口
class MyThread2 implements Runnable{
@Override
public void run() {
System.out.println("MyThread2");
}
}
//3.实现callable接口
class MyThread3 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("MyThread3");
return null;
}
}
推荐大家去b站看狂神的视频