多线程
创建线程的方式一,继承Thread类
package threadText;
//创建线程的方式一,继承thread类,重写run方法,使用Start开启
//注意,线程开启不一定立即执行,由cpu调度
public class ThreadText01 extends Thread{
//重写run方法
@Override
public void run() {
//run方法体
for (int i = 0; i < 10; i++) {
System.out.println("我是Thread"+i);
}
}
//main线程,主线程
public static void main(String[] args) {
//创建一个线程对象
ThreadText01 threadText01 = new ThreadText01();
//开启线程
threadText01.start();
for (int i = 0; i < 20; i++) {
System.out.println("我是main函数"+i);
}
}
}
同时下载3张图片
package threadText;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//练习Thread,同步下载图片
public class ThreadText02 extends Thread{
private String url;//网络图片地址
private String name;//文件名
public ThreadText02(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) {
ThreadText02 t1 = new ThreadText02("http://00.minipic.eastday.com/20170106/20170106165023_a14bfa564f46922451f7f43cd86ffd67_9.jpeg","9.jpeg");
ThreadText02 t2 = new ThreadText02("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1581247708&di=7b6c1bd628e2a29ac0ac724c9c8c9a8f&imgtype=jpg&er=1&src=http%3A%2F%2Fimg.kutoo8.com%2Fupload%2Fimage%2F72604229%2F14%2520%281%29_960x540.jpg","10.jpg");
ThreadText02 t3 = new ThreadText02("http://pic1.win4000.com/pic/3/23/3c411423997.jpg","11.jpg");
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("下载异常,webDownloader");
}
}
}
//下载顺序
//下载文件名为11.jpg
//下载文件名为9.jpeg
//下载文件名为10.jpg
//不一定是开启顺序
创建线程方式2,实现Runnable接口
package threadText;
//创建线程方式2,实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
public class ThreadText03 implements Runnable{
//重写run方法
@Override
public void run() {
//run方法体
for (int i = 0; i < 10; i++) {
System.out.println("我是Thread"+i);
}
}
//main线程,主线程
public static void main(String[] args) {
//创建一个runnable接口的实现类对象
ThreadText03 threadText03 = new ThreadText03();
/*
//创建线程对象,通过线程对象来开启我们的线程
Thread thread = new Thread(threadText03);
//开启
thread.start();
*/
//简写
new Thread(threadText03).start();
for (int i = 0; i < 20; i++) {
System.out.println("我是main函数"+i);
}
}
}
-
继承Thread类
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象. start()
- 不建议使用:避免0OP单继承局限性
-
实现Runnable接口
- 实现接口Runnable具有多线程能力
- 启动线程:传入目标对象+Thread对象.start()
推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
龟兔赛跑案例
package threadText;
/*
龟兔赛跑案例
1.设置赛道距离,逐渐接近
2.判断比赛是否结束
3.打印出胜利者
4.龟兔赛跑开始,模拟兔子睡觉
5.乌龟胜利
*/
public class ThreadText04 implements Runnable{
private static String winner;//静态保证只有一个
@Override
public void run() {
//赛道建立
for (int i = 0; i <= 100; i++) {
//判断是否跑完,结束程序
boolean gameover = gameover(i);
if(gameover){
break;
}
//兔子睡觉
if(Thread.currentThread().getName().equals("兔子")&&i>=3){//currentThread()返回正在执行线程的对象
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"跑了第"+i+"步");
}
}
//判断是否跑完
public boolean gameover(int i){
if(winner!=null){
return true;
}else if (i==100){
winner=Thread.currentThread().getName();
System.out.println(winner+"跑完了比赛");
return true;
}else {
return false;
}
}
public static void main(String[] args) {
ThreadText04 threadText04 = new ThreadText04();
new Thread(threadText04,"兔子").start();
new Thread(threadText04,"乌龟").start();
}
}
实现callable接口
- 实现callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行任务,ExecutorService ser= Executors.newFixedThreadPool(1);
- 提交执行,Future resutl=ser.submit(t1);
- 获取结果,Boolean r1 = resutl.get();
- 关闭服务,ser.shutdownNow();
- 好处
- 可以实现返回值
- 可以抛出异常
package threadText;
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 ThreadText05 implements Callable<Boolean> {
private String url;//网络图片地址
private String name;//文件名
public ThreadText05(String url, String name) {
this.name=name;
this.url=url;
}
@Override
public Boolean call() {
webDownloaderr webDownloader = new webDownloaderr();
webDownloader.downloader(url,name);
System.out.println("下载文件名为"+name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadText05 t1 = new ThreadText05("http://00.minipic.eastday.com/20170106/20170106165023_a14bfa564f46922451f7f43cd86ffd67_9.jpeg","9.jpeg");
ThreadText05 t2 = new ThreadText05("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1581247708&di=7b6c1bd628e2a29ac0ac724c9c8c9a8f&imgtype=jpg&er=1&src=http%3A%2F%2Fimg.kutoo8.com%2Fupload%2Fimage%2F72604229%2F14%2520%281%29_960x540.jpg","10.jpg");
ThreadText05 t3 = new ThreadText05("http://pic1.win4000.com/pic/3/23/3c411423997.jpg","11.jpg");
//创建执行任务
ExecutorService ser= Executors.newFixedThreadPool(1);
//提交执行
Future<Boolean> r1=ser.submit(t1);
Future<Boolean> r2=ser.submit(t2);
Future<Boolean> r3=ser.submit(t3);
//获取结果
Boolean rs1 = r1.get();
Boolean rs2 = r2.get();
Boolean rs3 = r2.get();
//关闭服务
ser.shutdownNow();
System.out.println(rs1);
System.out.println(rs2);
System.out.println(rs3);
}
}
//下载器
class webDownloaderr{
//下载方法
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("下载异常,webDownloader");
}
}
}
卖票,解决冲突
- 同步代码块
package threadText02;
/*
卖票实现,出现了安全问题
解决方法一,使用同步代码块
格式
synchronize(锁对象){
可能出现安全问题的代码(共享的代码)
}
注意
1.通过代码块中的锁对象,可以是任意对象
2.必须保证多个对象使用的锁对象是同一个
3.锁对象的原理,把同步代码锁住,只让一个线程在同步代码中执行
*/
public class Demo01Thread {
public static void main(String[] args) {
//创建Runnable接口,实现类对象
maipiao maipiao = new maipiao();
//创建Thread对象,构造方法中传递Runnable接口,实现类对象
Thread t1 = new Thread(maipiao);
Thread t2 = new Thread(maipiao);
Thread t3 = new Thread(maipiao);
//开启线程
t1.start();
t2.start();
t3.start();
}
}
/*
package threadText02;
/*
买票
public class maipiao implements Runnable{
//实现代码
Object obj=new Object();
private int ticket=100;
@Override
public void run() {
while(true){
//同步代码块
synchronized (obj){
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖出了第"+ticket+"票");
ticket--;
}
}
}
}
}
*/
- 同步方法
package threadText02;
/*
卖票实现,出现了安全问题
解决方法一,使用同步代码块
格式
synchronize(锁对象){
可能出现安全问题的代码(共享的代码)
}
注意
1.通过代码块中的锁对象,可以是任意对象
2.必须保证多个对象使用的锁对象是同一个
3.锁对象的原理,把同步代码锁住,只让一个线程在同步代码中执行
*/
public class Demo01Thread {
public static void main(String[] args) {
//创建Runnable接口,实现类对象
maipiao maipiao = new maipiao();
//创建Thread对象,构造方法中传递Runnable接口,实现类对象
Thread t1 = new Thread(maipiao);
Thread t2 = new Thread(maipiao);
Thread t3 = new Thread(maipiao);
//开启线程
t1.start();
t2.start();
t3.start();
}
}
/*
package threadText02;
买票
public class maipiao implements Runnable{
//实现代码
Object obj=new Object();
private int ticket=100;
@Override
public void run() {
while(true){
pp();
}
}
//同步代码块
public synchronized void pp(){
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖出了第"+ticket+"票");
ticket--;
}
}
}
*/
- lock
package threadText02;
/*
卖票实现,出现了安全问题
解决方法一,使用同步代码块
格式
synchronize(锁对象){
可能出现安全问题的代码(共享的代码)
}
注意
1.通过代码块中的锁对象,可以是任意对象
2.必须保证多个对象使用的锁对象是同一个
3.锁对象的原理,把同步代码锁住,只让一个线程在同步代码中执行
*/
public class Demo01Thread {
public static void main(String[] args) {
//创建Runnable接口,实现类对象
maipiao maipiao = new maipiao();
//创建Thread对象,构造方法中传递Runnable接口,实现类对象
Thread t1 = new Thread(maipiao);
Thread t2 = new Thread(maipiao);
Thread t3 = new Thread(maipiao);
//开启线程
t1.start();
t2.start();
t3.start();
}
}
/*
package threadText02;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
买票
public class maipiao implements Runnable{
//实现代码
Lock l =new ReentrantLock();
private int ticket=100;
@Override
public void run() {
while(true){
//lock
l.lock();
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖出了第"+ticket+"票");
ticket--;
}
l.unlock();
}
}
}
*/
线程之间的通信
- 生产者与消费者问题
package shengchanzhe;
/*
测试类
包含main方法,程序的入口
创建包子对象
创建包子铺线程,开启,生产包子
创建吃货线程,开启,吃包子
*/
public class Demo {
public static void main(String[] args) {
//创建包子对象
baoZi bz = new baoZi();
//创建包子铺线程,开启,生产包子
new BaoZiPu(bz).start();
//创建吃货线程,开启,吃包子
new ChiHuo(bz).start();
}
}
package shengchanzhe;
/*
消费者(吃货)类:是一个线程类,可以继承Thread
设置线程任务(run):吃包子
对包子的状态进行判断
false:没有包子
吃货调用woit方法进入等待状态
true:有包子
吃货吃包子
吃货吃完包子
修改包子的状态为false没有
吃货唤醒包子铺线程 ,生产包子
*/
public class ChiHuo extends Thread{
private baoZi bz;
public ChiHuo(baoZi bz) {
this.bz = bz;
}
@Override
public void run() {
// 吃货调用woit方法进入等待状态
while(true) {
synchronized (bz) {
if (bz.flag == false) {
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//被唤醒的线程
System.out.println("吃货正在吃" + bz.pi + bz.xian + "包子");
//吃货吃包子
// 吃货吃完包子
bz.flag = false;
//吃货唤醒包子铺线程 ,生产包子
bz.notify();
System.out.println("吃货吃完了" + bz.pi + bz.xian + "包子,开始生产");
System.out.println("=================================================");
}
}
}
}
package shengchanzhe;
/*
生产者,包子铺,是一个线程类,可以继承Thread
设置线程任务(run),生产包子
对包子的状态进行判定
turn,有包子,调用wait方法进入等待状态
false,没有包子
包子铺生产包子
增加一些趣味性,生产两种包子
有两种状态(i%2==0)
包子铺生产好包子
修改包子的状态为true
唤醒吃货线程吃包子
注意
包子线程与包子铺线程之间的关系————》通信(互斥)
必须使用同步技术,是这两个线程只有个在执行
锁对象必须保证唯一,可以使用包子对象作为锁对象
包子铺类与吃货类,需要把包子对象作为参数传递进来
1.需要在成员位置创建包子变量
2.使用带参构造方法,为这个包子赋值
*/
public class BaoZiPu extends Thread{
private baoZi bz;
int count=0;
public BaoZiPu(baoZi bz) {
this.bz = bz;
}
@Override
public void run() {
//让包子铺一直生产包子
while(true){
synchronized (bz) {
//turn,有包子,调用wait方法进入等待状态
if (bz.flag == true) {
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//被唤醒之后执行的代码
// false,没有包子
//增加一些趣味性,生产两种包子
if (count % 2 == 0) {
bz.pi = "薄皮";
bz.xian = "三鲜馅";
}else{
bz.pi = "厚皮";
bz.xian = "韭菜馅";
}
count++;
System.out.println("包子铺正在生产"+bz.pi+bz.xian+"包子");
//需要3000毫秒
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//包子铺生产好包子
// 修改包子的状态为true
bz.flag=true;
//唤醒吃货线程吃包子
bz.notify();
System.out.println("包子铺生产好了"+bz.pi+bz.xian+"包子,可以开始吃了");
}
}
}
}
package shengchanzhe;
/*
资源类,包子类
设置包子的属性
皮
馅
包子的状态,有、无
*/
public class baoZi {
//皮
String pi;
//馅
String xian;
//包子的状态
boolean flag=false;
}
线程状态
等待唤醒案例
package threadText;
/*
等待唤醒案例,线程之间的通信
创建一个顾客线程,告知老板需要的包子个数以及种类,调用wait方法,放弃cpu使用权,进入到WAITING状态(无线等待)
创建一个老板线程,花费5秒制作包子,报字制作完成,使用notify方法,告知顾客吃包子
可以加死循环,让其一直工作
注意
老板线程与顾客线程要使用同步代码块包裹
同步使用锁,必须对象一致
只用锁对象才能调用wait与notify方法
Object类中的方法
void wait() 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法
void notify() 唤醒正在等待对象监视器的单个线程,会唤醒wait之后的代码
*/
public class Demo02Thread {
public static void main(String[] args) {
//创建锁对象
Object ob=new Object();
//创建一个顾客线程
new Thread(){
@Override
public void run() {
//保证只有一个执行
synchronized(ob){
System.out.println("告知老板需要的包子个数以及种类");
try {
ob.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒之后执行,wait之后的代码
System.out.println("包子好了,开吃");
System.out.println("=======================================");
}
}
}.start();
//创建一个老板线程
new Thread(){
@Override
public void run() {
//保证只有一个执行
synchronized(ob){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("包子制作完成");
ob.notify();
}
}
}.start();
}
}
notify与notifyAll
package threadText;
/*
进入到TimeWaiting(计时等待)两种方式
1.使用使用sleep(long m),在睡醒后进入,Runnable或者Blocked状态
2.使用wait(long m),wait中设置的毫秒值结束之后,还没用被notify唤醒就会自动醒来,线程进入Runnable或者Blocked状态
*/
public class Demo03Thread {
public static void main(String[] args) {
//创建锁对象
Object ob=new Object();
//创建一个顾客线程
new Thread(){
@Override
public void run() {
//保证只有一个执行
synchronized(ob){
System.out.println("顾客一告知老板需要的包子个数以及种类");
try {
ob.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒之后执行,wait之后的代码
System.out.println("包子好了,顾客一开吃");
System.out.println("=======================================");
}
}
}.start();
//创建一个顾客线程
new Thread(){
@Override
public void run() {
//保证只有一个执行
synchronized(ob){
System.out.println("顾客二告知老板需要的包子个数以及种类");
try {
ob.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒之后执行,wait之后的代码
System.out.println("包子好了,顾客二开吃");
System.out.println("=======================================");
}
}
}.start();
//创建一个老板线程
new Thread(){
@Override
public void run() {
//保证只有一个执行
synchronized(ob){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("包子制作完成");
//ob.notify();//随机唤醒一个
ob.notifyAll();//全部唤醒
}
}
}.start();
}
}
线程池
- 线程池,就是荣来多个线程得容器,其中的线程可以反复使用,省去了频繁创建线程的对象操作,无需反复创建线程而消耗过多资源。
package threadText;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/*
线程池,JDK1.5之后出现
java.util.concurrent.Executors,线程池工厂类,用来生成线程池
Executors中的静态方法
static ExecutorService newFixedThreadPool(int nThreads) 创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程
参数
int nThreads,创建线程池的线程数
返回值
ExecutorService接口,返回ExecutorService接口实现类对象,我们可以使用ExecutorService接口接受(面向接口编程)
java.util.concurrent.ExecutorService
用来从线程池中获取线程,调用start方法执行线程
submit(Runnable task) 提交一个Runnable任务执行
关闭销毁线程池的方法
void shutdown()
线程池的使用步骤
1.使用线程池的工厂类Executors类中的方法newFixedThreadPool,创建固定数量的线程池
2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
3.使用ExecutorService中的方法submit,传递线程任务,开启线程
4.使用shutdown关闭线程(不建议使用)
*/
public class Demo04Thread {
public static void main(String[] args) {
//1.使用线程池的工厂类Executors类中的方法newFixedThreadPool,创建固定数量的线程池
ExecutorService es =Executors.newFixedThreadPool(2);
//3.使用ExecutorService中的方法submit,传递线程任务,开启线程
es.submit(new Demo04ThreadIpl());
//线程池会一直开启,使用完线程,会自动把线程归还给线程池,线程可以继续使用
es.submit(new Demo04ThreadIpl());
es.submit(new Demo04ThreadIpl());
//4.使用shutdown关闭线程(不建议使用)
es.shutdown();
}
}
/*
package threadText;
//2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
public class Demo04ThreadIpl implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行");
}
}
*/