狂神-线程笔记
线程
重点:线程的实现、线程同步
1、线程简介
一个进程有多个线程
2、实现
thread 继承thread类、实现runnable接口、实现callable
public class TestThread01 extends Thread{ /** * 方式1:继承Threa类,重新run()方法,调用start() * 总结: * 线程开启不一定是立即执行,是由CPU决定调度执行 */ @Override public void run() { //线程体 for (int i = 0; i < 20; i++) { System.out.println("小玉玉喜欢吃肉肉~~~~"+i); } } public static void main(String[] args) { //调用 TestThread01 testThread01 = new TestThread01(); testThread01.start(); //testThread01.start();这个是俩个线程是交替进行的 //testThread01.run();这个是走的单线程,先走的是run方法的内容 for (int i = 0; i < 2000; i++) { System.out.println("小绿喜欢吃白菜~~~"+i); } } }
练习多线程下载文件
public class TestThread02 extends Thread{ private String url; private String fileName; private String inThread; public TestThread02(String url, String fileName, String inThread) { this.url = url; this.fileName = fileName; this.inThread=inThread; } //调用线程 public static void main(String[] args) { TestThread02 t1 = new TestThread02("https://i0.hdslb.com/bfs/sycp/creative_img/202107/23d0390e2abd49a9b0cf3df55c4e7648.jpg", "1.jpg", "1"); TestThread02 t2 = new TestThread02("https://i0.hdslb.com/bfs/sycp/creative_img/202101/8b0e68d45dae53febbef907bdb4e61fe.jpg", "2.jpg", "2"); TestThread02 t3 = new TestThread02("https://i0.hdslb.com/bfs/sycp/creative_img/202110/95c7a7b3cf5a7f22b38f7e675443a16d.jpg", "3.png", "3"); t1.start(); t2.start(); t3.start(); } //线程体 @Override public void run() { downLoad(url,fileName); System.out.println("当前线程"+inThread); } //下载文件的方法 public void downLoad(String url,String fileName){ try { FileUtils.copyURLToFile(new URL(url),new File(fileName)); } catch (Exception e) { e.printStackTrace(); } } }
实现callable接口,推荐使用,因为java是单继承的会出现问题
// 方式2 实现 Runnable接口 推荐使用的方式
public class TestThread03 implements Runnable { @Override public void run() { //线程体 for (int i = 0; i < 20; i++) { System.out.println("小玉玉喜欢吃肉肉~~~~"+i); } } public static void main(String[] args) { //调用 TestThread03 t1 = new TestThread03(); TestThread03 t2 = new TestThread03(); new Thread(t1).start(); new Thread(t2).start(); for (int i = 0; i < 2000; i++) { System.out.println("小绿喜欢吃白菜~~~"+i); } } }
多线程实现龟兔赛跑案例
public class Race implements Runnable { //打印胜利者 private static String winner; public static void main(String[] args) { Race race=new Race(); //一条赛道 new Thread(race,"兔子").start(); new Thread(race,"乌龟").start(); } @Override public void run() { for (int i = 0; i <= 100; i++) { //模拟兔子休息 if(Thread.currentThread().getName().equals("兔子")&& i%20==0){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } //判断比赛是否结束 boolean flag=isEnd(i); if (flag){ //比赛结束停止比赛 break; } System.out.println(Thread.currentThread().getName()+"----->跑了"+i+"步"); } } //判断是否完成比赛 private boolean isEnd(int step){ if (winner!=null){ //已经有胜利者 return true; }{ if (step>=100){ //步数是100到达终点 winner=Thread.currentThread().getName(); System.out.println("Winner is"+winner); return true; } } return false; } }
静态代理模式
/** *代理结婚的案例说明静态代理模式 * 静态代理模式总结 * 真实对象和嗲了对象都要实现一个接口 * 代理对象要代理真实角色 * * 好处: * 代理对象可以做很多真实对象做不了的事情 * 代理对象专注做自己的事情 * * @author xiaoxiaoyu * @create 2021/11/2 * @since 1.0.0 */ public class StaticProxy { public static void main(String[] args) { // // new Thread(()->{ System.out.println("I love you"); }).start(); //thread代理真实对象 new Thread(new Runnable() { @Override public void run() { System.out.println("I love you"); } }).start(); new WeddingCompany(new You()).HappyMarry(); } } interface Marry{ void HappyMarry(); } class You implements Marry{ //真实角色,你去结婚 @Override public void HappyMarry() { System.out.println("小玉玉结婚啦~~~"); } } class WeddingCompany implements Marry{ //代理角色 婚庆公司帮你结婚 //代理谁---->真实目标角色 private Marry target; public WeddingCompany(Marry target) { this.target = target; } @Override public void HappyMarry() { //前期准备工作 befor(); target.HappyMarry(); //后期收尾工作 afters(); } private void befor() { System.out.println("结婚前,布置现场"); } private void afters() { System.out.println("结婚后,收尾工作"); } }
Lamda表达式
public class TestLambda1 { //3.静态内部类 static class Love2 implements Ilove{ @Override public void love(int i) { System.out.println("i love you--->"+i); } } public static void main(String[] args) { Ilove ilove=null; ilove= new Love2(); ilove.love(2); //4内部类 class Love3 implements Ilove{ @Override public void love(int i) { System.out.println("i love you--->"+i); } } ilove= new Love3(); ilove.love(3); //5,匿名内部类:没有类的名称,必须借助接口或者是父类 ilove=new Ilove() { @Override public void love(int i) { System.out.println("i love you--->"+i); } }; ilove.love(4); //6、用lambda简化 /* *总结: * lambda代码只有一行的时候,才能简化为一行,如果有多行,那么就用代码块包裹 * 前提是接口是函数式接口 * 多个参数也可以去掉参数类型,去掉就都去掉,必须加上括号 * */ ilove=(int i)-> { System.out.println("i love you--->"+i); }; ilove.love(5); } } //1,定义个函数式接口:接口中只有一个方法 interface Ilove{ void love(int i); } //2,实现类 class Love implements Ilove{ @Override public void love(int i) { System.out.println("i love you--->"+i); } }
线程状态:
新生状态---》就绪状态(start)--》 运行状态(run)------》阻塞状态(sleep、wait)-----》死亡状态deal()
1、测试stop线程
/** * 〈测试线程停止〉 * 1、建议线程正常停止=====》利用次数,不建议死循环 * 2、建议使用标志位 flag * 3、不要使用stop或者是destory等过时的方法或者JDK不建议使用的方法 * @author xiaoxiaoyu * @create 2021/11/4 * @since 1.0.0 */ public class TestStop implements Runnable { private boolean flag=true; @Override public void run() { int i = 0; while (flag){ System.out.println("runn。。thread"+i++); } } //在对外写一个停止的方法 public void stopThread(){ 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){ testStop.stopThread(); System.out.println("该停止啦"); } } } }
2、测试sleep
/** * 没有对象都有一把锁,sleep不会释放锁 * @author xiaoxiaoyu * @create 2021/11/4 * @since 1.0.0 */ public class TestSleep { public static void main(String[] args)throws Exception { //案例1:模拟倒计时 // tenDown(); //案例2:打印当前系统时间 Date date = new Date(System.currentTimeMillis());//当前系统时间 while (true){ System.out.println(new SimpleDateFormat("HH-mm-ss").format(date)); Thread.sleep(1000); date= new Date(System.currentTimeMillis());//当前系统时间 } } //倒计时的方法 public static void tenDown(){ int i=10; while (true){ try { Thread.sleep(1000); System.out.println(i--); if (i<=0){ break; } } catch (InterruptedException e) { e.printStackTrace(); } } } }
3、 线程礼让:yield 已让不一定能成功,看cpu心情,礼让之后会进入到就绪状态,重新进行竞争
4、线程join:相当于是插队,待此线程执行完成后其他线程才能执行,其他线程阻塞
public class TestJoin implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("我是VIP"+i); } } public static void main(String[] args) throws Exception{ TestJoin testJoin = new TestJoin(); Thread thread = new Thread(testJoin); thread.start(); for (int i = 0; i < 10; i++) { if (i==5){ //插队,不建议使用,会造成阻塞 thread.join(); } System.out.println("main"+i); } } }
5、线程状态
public class TestThreadState { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { for (int i = 0; i < 3; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("----------------"); }); Thread.State state = thread.getState(); System.out.println(state); //NEW thread.start(); state = thread.getState(); System.out.println(state); //启动线程 while (state!=Thread.State.TERMINATED){ //只要线程不终止就一直输出 Thread.sleep(100); //更新状态 state = thread.getState(); System.out.println(state); } //死亡后的线程不能再次进行启动 // thread.start(); } }
6、线程优先级
public class TestThreadProprity { public static void main(String[] args) { //主线程的优先级 System.out.println(Thread.currentThread().getName()+"----->"+Thread.currentThread().getPriority()); MyProprity myProprity= new MyProprity(); Thread t1 = new Thread(myProprity); Thread t2 = new Thread(myProprity); Thread t3 = new Thread(myProprity); //启动前进行设置优先级 t1.setPriority(3); t1.start(); t2.start(); t3.setPriority(9); t3.start(); //优先级高的不一定先执行,看CPU的心情,但是优先级高的执行的概率大一点 } } class MyProprity extends Thread{ @Override public void run() { //获取优先级 System.out.println(Thread.currentThread().getName()+"----->"+Thread.currentThread().getPriority()); } }
7、守护线程
public class TestThreadDaemon { public static void main(String[] args) { God god = new God(); Thread thread = new Thread(god); //默认是false 设置true为守护线程 thread.setDaemon(true); thread.start(); new Thread(new Yous()).start(); /** * 总结: * 线程分为守护线程和用户线程(普通线程) * 虚拟机必须确保用户线程执行完毕 * 虚拟机不用等待守护线程执行完毕 * 比如:后台操作日志、垃圾回收 */ } } class God implements Runnable{ @Override public void run() { while (true){ System.out.println("上帝在守护着你。。。。。。"); } } } class Yous implements Runnable{ @Override public void run() { for (int i = 0; i < 36500; i++) { System.out.println("---开开心心活着--"); } System.out.println("===good bye! world======="); } }
2、线程同步
2.1线程不安全案例:买票
public class UnsafeBuyTicket { //线程不安全,有负数 public static void main(String[] args) { //因为是多个线程操作的是同一个资源,所以需要new 一份 BuyTick statics = new BuyTick(); new Thread(statics,"A").start(); new Thread(statics,"B").start(); new Thread(statics,"C").start(); new Thread(statics,"D").start(); } } class BuyTick implements Runnable{ //票数 private int tickNum=10; //线程终止,用falg Boolean flag=true; @Override public void run() { //买票 while (flag){ try { tick(); } catch (InterruptedException e) { e.printStackTrace(); } } } private void tick() throws InterruptedException { if (tickNum<=0){ //没有票了 this.flag=false; return; } Thread.sleep(1000); //有票 System.out.println(Thread.currentThread().getName()+"----->"+tickNum--); } }
2.2线程不安全案例;取钱
//两个人去银行取钱,账户 //线程的内存都是各自处理的,都是从100这里拷贝过去的 public class UnsafeBank { public static void main(String[] args) { //账户 Account account=new Account(100,"结婚基金"); Drawing you = new Drawing(account, 50, "you"); Drawing grilFriend = new Drawing(account, 100, "grilFriend"); you.start(); grilFriend.start(); } } //账户 class Account { //余额 int money; //卡名 String name; public Account(int money,String name){ this.money=money; this.name=name; } } //银行:模拟取款:不涉及都多个线程操作同一个对象 class Drawing extends Thread{ Account account; //取了多少钱 int drawingMoney; //现在手里有多少钱 int nowMoney; public Drawing( Account account,int drawingMoney,String name){ super(name); this.account=account; this.drawingMoney=drawingMoney; } //取钱 public void GetMoney(){ if (account.money-drawingMoney<0){ System.out.println(Thread.currentThread().getName()+"----余额不足---"); return; } //放大问题的发生性 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //卡内余额=余额-你取走的钱 account.money=account.money-drawingMoney; //你手里的钱 nowMoney=nowMoney+drawingMoney; //this.getName()= Thread.currentThread().getName() System.out.println(account.name+"账户余额:"+account.money); System.out.println(this.getName()+"手里的钱有:"+nowMoney); } @Override public void run() { GetMoney(); } }
3、不安全的集合
public class UnsafeList { public static void main(String[] args) throws InterruptedException { List<String > list=new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } Thread.sleep(1000); System.out.println(list.size()); } }
将线程不安全的案例变为线程安全的案例
锁的这个对象,一定是进行增删改查的对象
买票
public class UnsafeBuyTicket { //线程不安全,有负数 public static void main(String[] args) { //因为是多个线程操作的是同一个资源,所以需要new 一份 BuyTick statics = new BuyTick(); new Thread(statics,"A").start(); new Thread(statics,"B").start(); new Thread(statics,"C").start(); new Thread(statics,"D").start(); } } class BuyTick implements Runnable{ //票数 private int tickNum=10; //线程终止,用falg Boolean flag=true; @Override public void run() { //买票 while (flag){ try { tick(); } catch (InterruptedException e) { e.printStackTrace(); } } } //synchronized(this)=BuyTick锁定的是类本身 private synchronized void tick() throws InterruptedException { if (tickNum<=0){ //没有票了 this.flag=false; return; } Thread.sleep(1000); //有票 System.out.println(Thread.currentThread().getName()+"----->"+tickNum--); } }
取钱
public class UnsafeBank { public static void main(String[] args) { //账户 Account account=new Account(1000,"结婚基金"); Drawing you = new Drawing(account, 50, "you"); Drawing grilFriend = new Drawing(account, 100, "grilFriend"); you.start(); grilFriend.start(); } } //账户 class Account { //余额 int money; //卡名 String name; public Account(int money,String name){ this.money=money; this.name=name; } } //银行:模拟取款:不涉及都多个线程操作同一个对象 class Drawing extends Thread{ Account account; //取了多少钱 int drawingMoney; //现在手里有多少钱 int nowMoney; public Drawing( Account account,int drawingMoney,String name){ super(name); this.account=account; this.drawingMoney=drawingMoney; } //取钱 public void GetMoney(){ synchronized (account){ if (account.money-drawingMoney<0){ System.out.println(Thread.currentThread().getName()+"----余额不足---"); return; } //放大问题的发生性 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //卡内余额=余额-你取走的钱 account.money=account.money-drawingMoney; //你手里的钱 nowMoney=nowMoney+drawingMoney; //this.getName()= Thread.currentThread().getName() System.out.println(account.name+"账户余额:"+account.money); System.out.println(this.getName()+"手里的钱有:"+nowMoney); } } @Override public void run() { GetMoney(); } }
ArrayList
public class UnsafeList { public static void main(String[] args) throws InterruptedException { List<String > list=new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ synchronized(list){ list.add(Thread.currentThread().getName()); } }).start(); } Thread.sleep(3000); System.out.println(list.size()); } }
或者是使用的是JUC下的包
import java.util.concurrent.CopyOnWriteArrayList; public class TestJUC { public static void main(String[] args) throws InterruptedException { CopyOnWriteArrayList<String > copyOnWriteArrayList=new CopyOnWriteArrayList<String>(); for (int i = 0; i < 1000; i++) { new Thread(()->{ copyOnWriteArrayList.add(Thread.currentThread().getName()); }).start(); } Thread.sleep(3000); System.out.println(copyOnWriteArrayList.size()); } }
3、死锁
//死锁:多个线程互相抱着对方需要的资源进行僵持 public class TestDealLock { public static void main(String[] args) { new Thread(new Deal2(0,"灰姑娘")).start(); new Thread(new Deal2(1,"白雪公主")).start(); /** * 打印结果 * 灰姑娘拿到了口红的锁 * 白雪公主拿到了镜子的锁 * 导致死锁原因: * 1、互斥条件 * 2、请求与保持 * 3、不剥夺条件 * 4、循环条件 * 只要是不满足其中一条就不会造成死锁的现象 */ /** * 优化后打印结果 * 灰姑娘拿到了口红的锁 * 白雪公主拿到了镜子的锁 * 白雪公主拿到了口红的锁 * 灰姑娘拿到了镜子的锁 */ } } //口红 class Lipsti{ } //镜子 class Mirror{ } class Deal2 implements Runnable { //独一份的资源 private static Lipsti lipsti=new Lipsti(); private static Mirror mirror=new Mirror(); private String name; private int choice; public Deal2(int choice,String name){ this.choice=choice; this.name=name; } @Override public void run() { if (choice==0){ synchronized (lipsti){ System.out.println(name+"拿到了口红的锁"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //会造成死锁 // synchronized (mirror){ // System.out.println(name+"拿到了镜子的锁"); // } } //优化后,不会产生死锁 synchronized (mirror){ System.out.println(name+"拿到了镜子的锁"); } }else { synchronized (mirror){ System.out.println(name+"拿到了镜子的锁"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //会造成死锁 // synchronized (lipsti){ // System.out.println(name+"拿到了口红的锁"); // } } //优化后,不会产生死锁 synchronized (lipsti){ System.out.println(name+"拿到了口红的锁"); } } } }
Lock和Synchronize的区别
public class TestLock { /** *总结Lock和Synchronize的区别 * 1、Lock是显示锁(手动进行开关),synchronize是隐式锁 * 2、Lock没有代码块和方法锁,synchronize有 * 3、Lock是JVm的,性能更好一点,有扩展性 * 4、优先使用顺序 * Lock》同步代码块》同步代码方法 * */ public static void main(String[] args) { BuyTick1 buyTick1 = new BuyTick1(); new Thread(buyTick1).start(); new Thread(buyTick1).start(); new Thread(buyTick1).start(); } } //买票的案例 class BuyTick1 implements Runnable{ //可重入锁 static ReentrantLock reentrantLock=new ReentrantLock(); int ticlNum=10; @Override public void run() { try { reentrantLock.lock(); while (true){ if (ticlNum<=0){ break; } try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ticlNum--); } }finally { reentrantLock.unlock(); } } }
线程通讯:使用wait和notify
管程法解决生产者和消费者问题
//生产者、消费者、产品、缓冲区 ---->管程法:利用缓冲区 public class TestPC { public static void main(String[] args) { SynContainet containet=new SynContainet(); new Thread(new Proctor(containet)).start(); new Thread(new Cutors(containet)).start(); } } class Proctor implements Runnable{ SynContainet containet; public Proctor(SynContainet containet) { this.containet = containet; } @Override public void run() { for (int i = 0; i < 100; i++) { containet.push(new Chick(i)); System.out.println("生产了"+i+"只鸡"); } } } class Cutors implements Runnable{ SynContainet containet; public Cutors(SynContainet containet) { this.containet = containet; } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("消费了"+containet.pop().id+"只鸡"); } } } class Chick { int id; public Chick(int id) { this.id = id; } } class SynContainet{ //缓冲区大小 Chick[] chicks=new Chick[10]; int count = 0; public synchronized void push(Chick chick){ //生产者 //判断能不能存 if(count==chicks.length){ //生产者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //没有生产满,就需要丢入产品 chicks[count]=chick; count++; //通知消费者去消费 this.notifyAll(); } //消费者 public synchronized Chick pop(){ if (count==0){ try { //消费者等待 this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //消费者去消费 count--; Chick chick = chicks[count]; //通知生产者生产 this.notifyAll(); return chick; } }
生产者和消费者解决方法2:信号灯发 :核心就是定义一个标志位
/** * 〈一句话功能简述〉<br> * 〈信号灯法解决生产者和消费者问题:定义一个flag〉 * @author xiaoxiaoyu * @create 2021/11/9 * @since 1.0.0 */ public class TestPC2 { public static void main(String[] args) { Perform perform=new Perform(); new Thread(new Player(perform)).start(); new Thread(new Watcher(perform)).start(); } } //演员 class Player implements Runnable{ Perform perform; public Player(Perform perform) { this.perform = perform; } @Override public void run() { for (int i = 0; i < 10; i++) { if (i%2==0){ //表演 perform.play("斗罗大陆**"); }else { perform.play("广告time。。。"); } } } } //观众 class Watcher implements Runnable{ Perform perform; public Watcher(Perform perform) { this.perform = perform; } @Override public void run() { for (int i = 0; i < 10; i++) { perform.watch(); } } } //表演 class Perform{ String dance; boolean flag=true; //true:演员表演,观众等待 //演员表演 public synchronized void play(String dance){ if (!flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("演员表演节目:"+dance); //通知观众观看 this.notifyAll(); this.dance=dance; this.flag=!this.flag; } public synchronized void watch(){ if (flag){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("观众观看"+dance); //通知演员表演 this.notifyAll(); this.flag=!this.flag; } }

浙公网安备 33010602011771号