Java多线程
1.网图下载
package com.chen.Test;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
//1.继承Thread类
public class ImageDownload extends Thread{
private String url; //下载路径
private String file; //保存到文件
public ImageDownload(String url,String file){
this.url=url;
this.file=file;
}
//2.重写run方法
public void run()
{
WebDownload webDownload=new WebDownload();
webDownload.imageDownload(this.url,this.file);
System.out.println("下载完成:"+this.file);
}
public static void main(String[] args)
{
//3.启动线程
new ImageDownload("https://pics1.baidu.com/feed/8c1001e93901213f4140e9d323774dd62e2e95e2.jpeg?token=495a45bbbace4d3ae51f9517ed0e0524","1.jpg").start();
new ImageDownload("https://p1.ssl.qhimgs1.com/sdr/400__/t0101ad6f6193917ca1.jpg","2.jpg").start();
new ImageDownload("https://p0.ssl.qhimgs1.com/sdr/400__/t0153e0c99a2877abce.jpg","3.jpg").start();
}
}
//工具类
class WebDownload{
public void imageDownload(String url,String file) {
try
{
FileUtils.copyURLToFile(new URL(url),new File(file));
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
2.龟兔赛跑
- 确定赛道
- 判断比赛是否结束
- 打印出胜利者
- 龟兔赛跑开始
- 根据故事情节,乌龟会赢,兔子需要睡觉
- 乌龟赢得比赛
package com.chen.Test;
public class Game implements Runnable{
private static String winner;
public void run()
{
//1.确定赛道
for(int i=0;i<=100;i++)
{
if(this.winner!=null)
break;
try
{ //2.兔子安排睡觉
if(Thread.currentThread().getName().equals("兔子")&&i%10==0)
Thread.sleep(1);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 跑了 "+i+" 步");
//3.确定胜利者
if(gameOver(i))
{
System.out.println("胜利者是:"+this.winner);
}
}
}
private boolean gameOver(int i)
{
if(winner!=null)
return false;
if(i>=100)
{
this.winner=Thread.currentThread().getName();
return true;
}
return false;
}
public static void main(String[] args)
{
Game game=new Game();
//龟兔开跑
new Thread(game,"乌龟").start();
new Thread(game,"兔子").start();
}
}
3.Callable接口
- 实现Callable接口需要返回值类型
- 重写call方法需要抛出异常
- 创建目标对象
- 创建执行服务
- 提交执行
- 获取结果
- 关闭服务
package com.chen.Test2;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.net.URL;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
//1.实现Callable接口
public class ImageDownload implements Callable<Boolean> {
private String url;
private String file;
public ImageDownload(String url,String file)
{
this.url=url;
this.file=file;
}
//2.重写call方法
public Boolean call()
{
try{
FileUtils.copyURLToFile(new URL(this.url),new File(this.file));
System.out.println("下载完成:"+ this.file);
}
catch (Exception e)
{
e.printStackTrace();
}
return true;
}
public static void main(String[] args)
{
//4.创建执行服务
ExecutorService es= Executors.newFixedThreadPool(3);
//5.创建目标对象并提交执行
Future<Boolean> f1=es.submit(new com.chen.Test2.ImageDownload("https://pics1.baidu.com/feed/8c1001e93901213f4140e9d323774dd62e2e95e2.jpeg?token=495a45bbbace4d3ae51f9517ed0e0524","1.jpg"));
Future<Boolean> f2=es.submit(new com.chen.Test2.ImageDownload("https://p1.ssl.qhimgs1.com/sdr/400__/t0101ad6f6193917ca1.jpg","2.jpg"));
Future<Boolean> f3=es.submit(new com.chen.Test2.ImageDownload("https://p0.ssl.qhimgs1.com/sdr/400__/t0153e0c99a2877abce.jpg","3.jpg"));
//6.获取结果
try
{
System.out.println(f1.get());
System.out.println(f2.get());
System.out.println(f3.get());
}
catch (Exception e)
{
e.printStackTrace();
}
//7.关闭服务
es.shutdownNow();
}
}
4.静态代理(实现Runnable接口的核心思想)
4.1例:婚庆公司
package com.chen.Test2;
public class StaticProxy {
public static void main(String[] args)
{
new MarryCompany(new You()).happyMarry();
}
}
//结婚接口
interface Marry{
public void happyMarry();
}
//结婚对象类
class You implements Marry{
public void happyMarry()
{
System.out.println("要结婚了,好开心~");
}
}
//婚庆公司类
class MarryCompany implements Marry{
private Marry target;
public MarryCompany(Marry target)
{
this.target=target;
}
public void happyMarry()
{
this.before();
target.happyMarry();
this.after();
}
private void before()
{
System.out.println("结婚之前,布置现场~");
}
private void after()
{
System.out.println("结婚之后,收尾款~");
}
}
4.2对比实现Runnable接口
new MarryCompany(new You()).happyMarry();
new Thread(new Game()).start();
Thread ----> MarryCompany
You -----> Game
happyMarry() ---->start() [类似,Thread类中不是直接使用接口中的方法]
核心思想:静态代理类帮助目标对象完成其自身不需要完成的事,使之能专注于做自己的事
5.多线程的停止
- 不推荐使用JDK的stop(),destroy()方法。【已废弃】
- 推荐线程自己停下来
- 建议使用一个标志位作为终止变量,当flag为false时,则终止线程
标志位的使用:
package com.chen.Test2;
public class Demo1 implements Runnable{
//设置停止标志位
private boolean flag=true;
public void run(){
int i=0;
while(flag)
{
System.out.println("thread--->"+i++);
}
}
public static void main(String[] args) {
Demo1 demo=new Demo1();
new Thread(demo).start();
for (int i = 0; i < 1000; i++) {
if(i==500)
demo.flag=false;
System.out.println("main--->"+i);
}
}
}
6.线程礼让—yield()方法
- 礼让线程,让当前线程停止执行,但不阻塞
- 将线程从运行状态转为就绪状态
- 让CPU从新调度,礼让不一定成功
package com.chen.Test2;
public class TestYield {
public static void main(String[] args) {
Test t=new Test();
new Thread(t,"a").start();
new Thread(t,"b").start();
}
}
class Test implements Runnable{
public void run(){
System.out.println(Thread.currentThread().getName()+"线程开始~");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程结束~");
}
}
7.线程强制执行—join()方法
- join合并线程,待此线程执行完后再执行其他线程,其他线程阻塞
- 可以想象成插队
package com.chen.Test2;
public class TestJoin implements Runnable{
public void run()
{
for (int i = 0; i < 1000; i++) {
System.out.println("vip来了--->"+i);
}
}
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(new TestJoin());
t.start();
for (int i = 0; i < 500; i++) {
if(i==200)
t.join();
System.out.println("main--->"+i);
}
}
}
8.线程的状态
- New(新建)
- Runnable(可运行)
- Blocked(阻塞)
- Waiting(等待)
- Timed waiting(计时等待)
- Terminated(终止)
观测线程状态
package com.chen.Test2;
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
for (int i = 0; i < 50; i++) {
if(i==30) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
System.out.println(t.getState());
t.start();
System.out.println(t.getState());
Thread.State state=t.getState();
while(state!=Thread.State.TERMINATED)
{
Thread.sleep(10);
state=t.getState();
System.out.println(state);
}
}
}
9.线程的优先级
-
范围:1~10
-
线程优先级低意味着获得调度的概率低,并不是优先级低就不会被调度
设置线程优先级:
package com.chen.Test2;
public class TestPriority implements Runnable{
public void run()
{
System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
}
public static void main(String[] args) {
TestPriority test=new TestPriority();
System.out.println("main priority--->"+Thread.currentThread().getPriority());
Thread t1=new Thread(test);
Thread t2=new Thread(test);
Thread t3=new Thread(test);
Thread t4=new Thread(test);
Thread t5=new Thread(test);
Thread t6=new Thread(test);
t1.setPriority(1);
t2.setPriority(5);
t3.setPriority(7);
t4.setPriority(8);
t5.setPriority(9);
t6.setPriority(2);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
10.守护线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 如,后台记录日志、监控内存、垃圾回收等
设置守护线程:
package com.chen.Test2;
public class TestGuard {
public static void main(String[] args)
{
Thread t1=new Thread(new God());
t1.setDaemon(true); //设置守护线程;默认为false,即用户线程
t1.start();
new Thread(new Human()).start();
}
}
class God implements Runnable{
public void run(){
while(true)
{
System.out.println("上帝保佑着你~");
}
}
}
class Human implements Runnable{
public void run(){
for (int i = 0; i < 36500; i++) {
System.out.println("开心的活着~");
}
System.out.println("-------------goodbye world-----------------");
}
}
11.三大不安全线程实例
11.1火车站购票
package com.chen.Test2;
public class Station {
public static void main(String[] args) {
Machine m=new Machine();
new Thread(m,"小明").start();
new Thread(m,"小黄").start();
new Thread(m,"黄牛党").start();
}
}
class Machine implements Runnable{
private int ticketNum=10; //放票总数
private boolean flag=true; //线程停止标志
public void run(){
while(flag)
buy();
}
private void buy() {
if(this.ticketNum<=0)
{
flag=false;
return;
}
try{
Thread.sleep(100);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 抢到了 第"+this.ticketNum--+"张票");
}
}
11.2银行取钱
package com.chen.Test2;
public class UnsafeBank {
public static void main(String[] args) {
Account account=new Account(50,"创业基金");
Thread t1=new Drawer(account,50,"小王");
Thread t2=new Drawer(account,30,"小李");
t1.start();
t2.start();
}
}
class Account{
private int money; //账户余额
private String name; //账户名
public Account(int money,String name)
{
this.money=money;
this.name=name;
}
public int getMoney() {
return money;
}
public String getName() {
return name;
}
public void setMoney(int money) {
this.money = money;
}
}
//取钱平台
class Drawer extends Thread{
private Account account;
private int having; //手里拥有的钱
private int drawing; //取钱数
public Drawer(Account account,int drawing,String name)
{
super(name);
this.account=account;
this.drawing=drawing;
}
public void run()
{
if(account.getMoney()-drawing<0)
{
System.out.println("余额不足~");
return;
}
try{
Thread.sleep(100);
}
catch (Exception e)
{
e.printStackTrace();
}
this.account.setMoney(this.account.getMoney()-drawing);
this.having=this.having+drawing;
System.out.println(this.getName()+" 手里有 "+ this.having);
System.out.println("账户余额为:"+this.account.getMoney());
}
}
11.3不安全的List集合
package com.chen.Test2;
import java.util.LinkedList;
import java.util.List;
public class UnsafeList {
public static void main(String[] args) {
List<String> list=new LinkedList();
for(int i=0;i<10000;i++)
{
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try{
Thread.sleep(3000);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println(list.size());
}
}
11.4sleep方法的作用
放大问题发生的可能性
12.三大不安全线程实例—>安全
12.1火车站取票
package com.chen.Test2;
public class Station {
public static void main(String[] args) {
Machine m=new Machine();
new Thread(m,"小明").start();
new Thread(m,"小黄").start();
new Thread(m,"黄牛党").start();
}
}
class Machine implements Runnable{
private int ticketNum=10;
private boolean flag=true;
public void run(){
while(flag)
buy();
}
private synchronized void buy() {
if(this.ticketNum<=0)
{
flag=false;
return;
}
try{
Thread.sleep(100);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 抢到了 第"+this.ticketNum--+"张票");
}
}
12.2银行取钱
package com.chen.Test2;
public class UnsafeBank {
public static void main(String[] args) {
Account account=new Account(50,"创业基金");
Thread t1=new Drawer(account,50,"小王");
Thread t2=new Drawer(account,30,"小李");
t1.start();
t2.start();
}
}
class Account{
private int money;
private String name;
public Account(int money,String name)
{
this.money=money;
this.name=name;
}
public int getMoney() {
return money;
}
public String getName() {
return name;
}
public void setMoney(int money) {
this.money = money;
}
}
class Drawer extends Thread{
private Account account;
private int having;
private int drawing;
public Drawer(Account account,int drawing,String name)
{
super(name);
this.account=account;
this.drawing=drawing;
}
public void run()
{
synchronized (account)
{
if(account.getMoney()-drawing<0)
{
System.out.println("余额不足~");
return;
}
try{
Thread.sleep(100);
}
catch (Exception e)
{
e.printStackTrace();
}
this.account.setMoney(this.account.getMoney()-drawing);
this.having=this.having+drawing;
System.out.println(this.getName()+" 手里有 "+ this.having);
System.out.println("账户余额为:"+this.account.getMoney());
}
}
}
12.3安全的List集合
package com.chen.Test2;
import java.util.LinkedList;
import java.util.List;
public class UnsafeList {
public static void main(String[] args) {
List<String> list=new LinkedList();
for(int i=0;i<10000;i++)
{
new Thread(()->{
synchronized (list)
{
list.add(Thread.currentThread().getName());
}
}).start();
}
try{
Thread.sleep(3000);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println(list.size());
}
}
13.同步方法和同步块
同步方法:
- synchronized
- 锁住的是this对象
同步块:
- synchronized(obj)
- obj称为同步监视器,可以为任意对象
14.测试JUC安全类型集合
package com.chen.Test2;
import java.util.LinkedList;
import java.util.concurrent.CopyOnWriteArrayList;
public class TestJUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list= new CopyOnWriteArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try{
Thread.sleep(3000);
}
catch (Exception e)
{
e.printStackTrace();
}
System.out.println(list.size());
}
}
15.死锁
15.1产生死锁的四个必要条件
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源阻塞时,对已获得的资源保持不放
- 不剥夺条件:进程已获得的资源,在未使用完之前不能强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
破坏其一即可避免死锁
15.2死锁的演示
package com.chen.Demo01;
public class DeadLock{
public static void main(String[] args) {
Thread t1=new Makeup("girl01",0);
Thread t2=new Makeup("girl02",1);
t1.start();
t2.start();
}
}
class Mirror{
}
class Lipstick{
}
class Makeup extends Thread{
//用static使资源只有一份
private static Mirror mirror=new Mirror();
private static Lipstick lipstick=new Lipstick();
private String name;
private int chioce;
public Makeup(String name,int chioce)
{
super(name);
this.chioce=chioce;
}
@Override
public void run() {
makeup(this.chioce);
}
public void makeup(int chioce)
{
//先获取镜子,再获取口红
if(chioce==0)
{
synchronized (mirror)
{
System.out.println(Thread.currentThread().getName()+" 获得了镜子");
try{
Thread.sleep(1000);
}
catch (Exception e)
{
e.printStackTrace();
}
synchronized (lipstick)
{
System.out.println(Thread.currentThread().getName()+" 获得了口红");
}
}
}
//先获取口红再获取镜子
else
{
synchronized (lipstick)
{
System.out.println(Thread.currentThread().getName()+" 获得了口红");
try{
Thread.sleep(1000);
}
catch (Exception e)
{
e.printStackTrace();
}
synchronized (mirror)
{
System.out.println(Thread.currentThread().getName()+" 获得了镜子");
}
}
}
}
}
16.可重入锁(ReentrantLock)
16.1示例
package com.chen.Demo01;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
Station s = new Station();
new Thread(s).start();
new Thread(s).start();
new Thread(s).start();
}
}
class Station implements Runnable{
private int ticketNum=10;
private ReentrantLock rl=new ReentrantLock();
@Override
public void run() {
while(true)
{
try
{
rl.lock();
if(this.ticketNum>0)
{
try{
Thread.sleep(1000);
}
catch(Exception e)
{
e.printStackTrace();
}
buy();
}
else
break;
}
finally
{
rl.unlock();
}
}
}
private void buy()
{
System.out.println(this.ticketNum--);
}
}
16.2锁的选择
- Lock是显式锁(手动开启和关闭锁),synchronized是隐式锁,出了作用域自动释放
- Lock只有代码块,synchronized有代码块和方法锁
- 使用Lock锁,JVM花费较少时间来调度线程,性能更好。并且有更好的拓展性(提供更多的子类)
- 优先使用顺序:
- Lock>同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)
17.生产者消费者问题
目的:实现线程间的通信
17.1管程法
package com.chen.Demo01;
public class TestPC {
public static void main(String[] args) {
Containner containner=new Containner();
new Productor(containner).start();
new Customer(containner).start();
}
}
//生产者
class Productor extends Thread{
private Containner containner;
public Productor(Containner containner)
{
this.containner=containner;
}
@Override
public void run() {
for(int i=1;i<=100;i++)
{
containner.push(new Product(i));
}
}
}
//消费者
class Customer extends Thread{
private Containner containner;
public Customer(Containner containner)
{
this.containner=containner;
}
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
containner.pop();
}
}
}
//产品
class Product{
private int id;
public Product(int id)
{
this.id=id;
}
public int getId() {
return id;
}
}
//容器
class Containner{
private Product[] containner=new Product[10];
private int count=0;
public synchronized void push(Product product)
{
//容器满,生产者等待
if(count==10)
{
try
{
wait();
}
catch (Exception e)
{
e.printStackTrace();
}
}
//生产者将产品放入容器,通知消费者消费
count++;
containner[count-1]=product;
System.out.println("放入了第 "+product.getId()+" 个产品");
notifyAll();
}
public synchronized Product pop()
{
//容器为空,消费者等待
if(count==0)
{
try{
wait();
}
catch (Exception e)
{
e.printStackTrace();
}
}
//消费者取走产品,通知生产者生产
count--;
Product product=containner[count];
containner[count]=null;
System.out.println("拿走了第 "+product.getId()+" 个产品");
notifyAll();
return product;
}
}
17.2信号灯法
package com.chen.Demo01;
public class TestPC2 {
public static void main(String[] args) {
Tv tv=new Tv();
new Actor(tv).start();
new Wacher(tv).start();
}
}
//生产者---演员
class Actor extends Thread{
private Tv tv;
public Actor(Tv tv)
{
this.tv=tv;
}
@Override
public void run() {
for(int i=0;i<20;i++)
{
if(i%2==0)
tv.act("你好生活~");
else
tv.act("新闻联播~");
}
}
}
//消费者---观众
class Wacher extends Thread{
private Tv tv;
public Wacher(Tv tv)
{
this.tv=tv;
}
@Override
public void run() {
for(int i=0;i<20;i++)
{
tv.watch();
}
}
}
//产品---TV
class Tv{
private String tv; //产品
private boolean flag=true; //信号变量
public synchronized void act(String show)
{
if(!flag)
{
try{
wait();
}
catch (Exception e)
{
e.printStackTrace();
}
}
this.tv=show;
System.out.println("演员表演了 "+show);
this.flag=!this.flag;
notifyAll();
}
public synchronized void watch()
{
if(flag)
{
try
{
wait();
}
catch(Exception e)
{
e.printStackTrace();
}
}
System.out.println("观众观看了 "+this.tv);
this.flag=!this.flag;
notifyAll();
}
}
18.线程池的使用
package com.chen.Demo01;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PoolTest implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
ExecutorService es= Executors.newFixedThreadPool(10);
new Thread(new PoolTest()).start();
new Thread(new PoolTest()).start();
new Thread(new PoolTest()).start();
new Thread(new PoolTest()).start();
es.shutdown();
}
}
19.总结—实现多线程的三种方式
package com.chen.Demo01;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Summarize {
public static void main(String[] args) throws ExecutionException, InterruptedException {
new Thread(new Test1()).start();
new Test2().start();
FutureTask<String> f=new FutureTask<String>(new Test3());
new Thread(f).start();
System.out.println(f.get());
}
}
//实现Runnable接口
class Test1 implements Runnable{
@Override
public void run() {
System.out.println("Runnable");
}
}
//继承Thread类
class Test2 extends Thread{
@Override
public void run() {
System.out.println("Thread");
}
}
//实现Callable接口
class Test3 implements Callable<String>{
@Override
public String call() throws Exception {
return "callable";
}
}