java多线程
2022/7/4 1:23:10
本文主要是介绍java多线程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
进程:程序的一次执行,是系统资源分配的单位,进程包含多个线程
真正的多线程是多个cpu,即多核,如服务器;而模拟出来的多线程只有一个cpu,一个cpu同时只能做一件事,由于切换速度非常快,人察觉不到变化,所以看上去好像是同时进行的。
如何创建线程
-
继承Thread类,这个类本身实现了Runable接口,不建议使用,避免单继承的局限性
public class threadDemo extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("wq"); } } }
-
实现Runable接口,推荐使用,更加灵活
public class loader implements Runnable{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("王琴"); } } }
开启线程
start()方法
public static void main(String[] args) { // write your code here threadDemo th1=new threadDemo(); loader l1=new loader(); new Thread(l1).start(); for (int i = 0; i < 100; i++) { System.out.println("123"); } th1.start(); }
龟兔赛跑
public class race implements Runnable{ private static String winner; @Override public void run() { for (int i = 0; i <=100; i++) { System.out.println(Thread.currentThread().getName()+"跑了"+i+"步"); boolean f=gameover(i); if(f){ break; } } } public boolean gameover(int step){ if(winner!=null){ return true; }{ if(step>=100){ winner=Thread.currentThread().getName(); System.out.println("winner is"+winner); return true; } } return false; } public static void main(String[] args) { race r=new race(); new Thread(r,"兔子").start(); new Thread(r,"乌龟").start(); } }
静态代理之婚庆公司
这是线程的底部实现原理Thread类就好比agent
//静态代理简单例子 //线程的底部实现原理 public class test02 { public static void main(String[] args) { you you=new you(); new agent(you).getMarried(); } } interface marry{ void getMarried(); } /** * 人间四大喜事: * 久旱逢甘露 * 他乡遇故知 * 洞房花烛夜 * 金榜题名时 */ class you implements marry{ @Override public void getMarried() { System.out.println("王琴结婚了"); } } class agent implements marry{ private marry marry; public agent(marry m){ this.marry=m; } @Override public void getMarried() { before(); marry.getMarried(); after(); } private void after() { System.out.println("婚礼结束,收尾款"); } private void before() { System.out.println("婚礼开始,准备结婚"); } }
线程停止
不建议使用stop和destroy方法
通过设立标志位让线程执行,
然后改变标志位来停止线程
public class Stopth implements Runnable{ //线程的停止不建议使用stop和destroy方法 //可以通过设立标志位,再改变标志位来停止 boolean flag=true; @Override public void run() { while (flag){ System.out.println("线程执行"); } } public void stop(){ this.flag=false; } public static void main(String[] args) { Stopth s=new Stopth(); new Thread(s).start(); for (int i = 0; i < 100; i++) { System.out.println("main线程"+i); if(i==80){ s.stop(); System.out.println("线程停止-------"); } } } }
线程休眠sleep
休眠过后的线程处于就绪状态
sleep可以模拟网络延时,倒计时等
每个对象都有一把锁,sleep不会释放锁
public class Sleepth { public static void main(String[] args) throws InterruptedException { //模拟时钟 Date date=new Date(System.currentTimeMillis()); while (true) { System.out.println(new SimpleDateFormat("hh:dd:ss").format(date)); Thread.sleep(1000); date=new Date(System.currentTimeMillis()); if(date.getSeconds()==00){ break; } } //模拟倒计时 public static void timedown() throws InterruptedException { int num=10; while (true){ Thread.sleep(1000); System.out.println(num--); if(num==0){ break; } } } }
线程礼让yield
礼让不会阻塞
礼让不一定成功,看cpu心情
//线程礼让,礼让不一定成功,看cpu心情 public class Yieldth implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"线程开始"); Thread.yield(); System.out.println(Thread.currentThread().getName()+"线程停止=========="); } public static void main(String[] args) { Yieldth t=new Yieldth(); new Thread(t,"李四").start(); new Thread(t,"王五").start(); //new Thread(t,"箱子").start(); } }
线程插队join
join方法合并线程,让其他线程阻塞,太霸道了,强行插队~~
public class Jointh implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("我是vip线程,请你让开~~"); } } public static void main(String[] args) throws InterruptedException { Jointh t=new Jointh(); Thread th=new Thread(t); th.start(); for (int i = 0; i <20 ; i++) { th.join();//vip线程插队,必须执行完vip线程,main线程才能执行 System.out.println("我是main线程~~"); } } }
线程状态state
NEW==== 新生状态,开启前 | ||
---|---|---|
RUNNABLE===运行,开启后 | ||
BLOCKED===阻塞 | ||
WAITING===等待,一直等 | ||
TIMED_WAITING ===定时等,为期不候 | ||
TERMINATED===终止,死亡,不能再次开启 |
public class Stateth { public static void main(String[] args) { Thread thread=new Thread(()->{ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程运行了"); }); thread.start(); Thread.State state = thread.getState(); System.out.println(state); while ( state!= Thread.State.TERMINATED){ //更新线程状态 state = thread.getState(); System.out.println(state); } } }
线程优先级
-
优先级值1~10
-
线程优先级设置,一定先设置优先级再开启线程
-
优先级低的不一定后执行,只是执行的概率更低
-
默认的线程优先级是5
public class Priorityth { public static void main(String[] args) { //默认线程优先级数---5,主线程一定最先执行 System.out.println("main线程====="+Thread.currentThread().getPriority()); myThread th=new myThread(); Thread t1=new Thread(th); Thread t2=new Thread(th); Thread t3=new Thread(th); Thread t4=new Thread(th); Thread t5=new Thread(th); //先设置优先级再开启线程 //优先级高的不一定先执行 t1.setPriority(5); t1.start(); System.out.println("main线程====="+Thread.currentThread().getPriority()); t2.setPriority(Thread.MAX_PRIORITY); t2.start(); t3.setPriority(7); t3.start(); t4.setPriority(Thread.MIN_PRIORITY); t4.start(); t5.setPriority(9); t5.start(); } } class myThread implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"======="+Thread.currentThread().getPriority()); } 结果: main线程=====5 main线程=====5 Thread-4=======9 Thread-1=======10 Thread-3=======1 Thread-0=======5 Thread-2=======7
结果分析:主线程优先级不是最高的但主线程一定最先执行***
虽然设置了优先级,但并不一定优先级的高低执行,所以优先级并不是绝对的
守护线程deamon
- 线程分为用户线程和守护线程
- 守护线程守护着用户线程的,直到用户线程死亡,就自动停止
- 没有设置为守护线程的都是用户线程
public class Deamonth { public static void main(String[] args) { God god=new God(); You you=new You(); Thread t1=new Thread(god); t1.setDaemon(true);//设置守护线程,默认为false t1.start(); new Thread(you).start();//用户线程,没有设置为守护线程的都是用户线程 } } //守护线程:守护线程守护者用户线程,直到用户线程终止才停止 class God implements Runnable{ @Override public void run() { while (true){ System.out.println("每天都要开开心心的,我一直都在守护,直到你离开"); } } } class You implements Runnable{ @Override public void run() { for (int i = 0; i <=30000; i++) { System.out.println("虽然生活艰难,但我依然坚持开心活着"); if(i==30000){ System.out.println("我要离开这个世界了"); break; } } } }
死锁
死锁的四个必要条件:
-
互斥条件:一个资源每次只能被一个进程使用
-
请求与保持:一个进程因请求资源而阻塞时,对已获得的资源保持不放
-
不可剥夺条件:对已获得资源,在未使用完前,不可被强制剥夺
-
循环等待条件:互相持有对方的资源保持不放,导致多个进程循环等待。
注:只要想办法突破其中一个或多个条件就能避免死锁的发生。
//死锁:互相持有对方的资源,僵持着 public class DeadLock { public static void main(String[] args) { MakeUp girl1=new MakeUp(0,"丑小鸭"); MakeUp girl2=new MakeUp(1,"白雪公主"); girl1.start(); girl2.start(); } } //口红 class LipStick{ } //镜子 class Mirror{ } //化妆 class MakeUp extends Thread{ //化妆需要口红和镜子 LipStick lipStick=new LipStick(); Mirror mirror=new Mirror(); int choice; public MakeUp(int choice,String name){ super(name);//传入线程名字 this.choice=choice; } public void makeup() throws InterruptedException { //开始化妆 //开始 A拿了口红,B拿了镜子 //之后 A又想要镜子,B又想要口红 //最后 A拿着口红又想要镜子,B拿着镜子又想要口红 //双方僵持不下,导致死锁 if(this.choice==0){ //拿着口红还要拿镜子 synchronized (lipStick){ System.out.println(this.getName()+"获得口红"); Thread.sleep(1000); synchronized (mirror){ System.out.println(this.getName()+":我还想要镜子"); } } }else { synchronized (mirror){ System.out.println(this.getName()+"获得镜子"); Thread.sleep(2000); synchronized (lipStick){ System.out.println(this.getName()+":我还想要口红"); } } } } @Override public void run() { super.run(); try { makeup(); } catch (InterruptedException e) { e.printStackTrace(); } } }
解决办法:使用完资源后放下再去拿其他资源
if(this.choice==0){ //用完口红后释放,再去拿镜子 synchronized (lipStick){ System.out.println(this.getName()+"获得口红"); Thread.sleep(1000); } synchronized (mirror){ System.out.println(this.getName()+":我还想要镜子"); } }else { synchronized (mirror){ System.out.println(this.getName()+"获得镜子"); Thread.sleep(2000); } synchronized (lipStick){ System.out.println(this.getName()+":我还想要口红"); } }
三大线程不安全案例使用同步机制解决
同步就是让多个线程排队使用资源,就不会出现资源抢夺。
由于每个线程都有自己的工作内存,控制不当就容易造成数据混乱。
每个对象都有一把锁,同步就是拿到对象的锁,等线程使用完对象后,紧接着下一个线程
- 同步代码块:synchronized (object){}
- 同步方法:public synchronized void test(){}
//不安全取钱 public class AcountTh { public static void main(String[] args) { Account account=new Account(200,"医保基金"); drawMoney d1=new drawMoney(account,100,"老婆"); drawMoney d2=new drawMoney(account,50,"老公"); d1.start(); d2.start(); } } //账户 class Account{ int money;//余额 String name;//卡号 public Account( int money,String name){ this.money=money; this.name=name; } } //模拟取款 class drawMoney extends Thread{ private Account account; private int outMoney;//要取出的钱 private int nowMoney;//手里的钱 private String name;//谁取得钱 public drawMoney(Account account,int outMoney,String name){ super(name); this.account=account; this.outMoney=outMoney; } //取钱 @Override public void run() { synchronized (account) {//拿到account的锁,让线程可以排队使用 if (this.account.money - this.outMoney < 0) { System.out.println(this.getName() + ":钱不够了"); return; }//Thread.currentThread().getName() = this.getName() try { Thread.sleep(1000);//放大问题的发生 } catch (InterruptedException e) { e.printStackTrace(); } this.account.money = this.account.money - this.outMoney;//更新余额 this.nowMoney = this.nowMoney + this.outMoney; System.out.println(this.getName() + ":取了" + nowMoney); System.out.println(this.getName() + ":卡里还有" + this.account.money); } } }
//不安全的买票 //线程不安全,有负数 public class BuyTicket { public static void main(String[] args) { Station station=new Station(); new Thread(station,"幸运的你").start(); new Thread(station,"可怜的我们").start(); new Thread(station,"可恶的黄牛党").start(); } } class Station implements Runnable{ //车票数 private int tickets=10; boolean flag=true;//设置停止线程的标志位 @Override public synchronized void run() {//synchronized默认锁的是this while (flag){ try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } public void buy() throws InterruptedException { if(tickets<=0){ System.out.println("票卖完了"); flag=false; } Thread.sleep(100);//模拟延时放大问题的发生性 System.out.println(Thread.currentThread().getName()+"买了第"+tickets--); } }
//不安全的集合----List //会导致多个线程添加到同一个空间 public class UnSafeList { public static void main(String[] args) throws InterruptedException { List<String> list=new ArrayList<String>(); for (int i = 0; i < 1000; i++) { new Thread(() -> { synchronized (list) {//如果不将list锁住,多个线程可能会被同时添加到list的同一个空间 list.add(Thread.currentThread().getName()); } }).start(); } Thread.sleep(1000); System.out.println(list.size()); } }
线程安全的集合
//juc(java.util.concurrent并发包)安全类型 public class JUCtest { public static void main(String[] args) throws InterruptedException { //CopyOnWriteArrayList是写好的线程安全的,而list是线程不安全的 CopyOnWriteArrayList<String> list=new CopyOnWriteArrayList<String>(); for (int i = 0; i < 1000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); } Thread.sleep(1000); System.out.println(list.size()); } }
加锁的两种方式
加锁是为了拿到对象的锁,避免线程抢夺资源,实现同步
显示加锁:使用juc包下的ReentrantLock(可重入锁)创建一把锁,再使用lock()方法,最后使用unlock()关闭锁
隐式加锁:使用同步代码块或同步方法,上面已经介绍了这种方法。
//显示加锁测试 public class testLock { public static void main(String[] args) { buyTicket buy=new buyTicket(); new Thread(buy,"我").start(); new Thread(buy,"你").start(); new Thread(buy,"他").start(); } } //显示加锁,使用juc并发编程中的lock /* private final ReentrantLock lock=new ReentrantLock();//创建一把可重入锁 public void run() { lock.lock();//加锁 try{ 要锁的代码块 }finally { lock.unlock();//释放锁 } } * */ class buyTicket implements Runnable{ private int tickets=10; boolean flag=true; private final ReentrantLock lock=new ReentrantLock();//创建一把可重入锁 @Override public void run() { while (flag) { lock.lock();//加锁 try{ if(tickets>0){ System.out.println("抢到了第"+tickets--+"张票"); } else { flag=false; } }finally { lock.unlock();//释放锁 } } } }
synchronized与lock的对比
- lock是显示锁,需要手动开启与关闭,而synchronized是隐式锁,出了作用域后自动释放
- synchronized可以锁住方法块也能锁住代码块,而lock只能锁住代码块
- 使用lock可以减少JVM调度线程的时间
- 使用建议:lock>synchronized代码块>synchronized方法
线程通信之生产者,消费者问题
生产者生产了产品,消费者才能消费产品,但是如何让消费者知道有产品可以消费了。
又如何让生产者知道产品消费完了,需要生产产品了呢?
这就要线程之间相互通信,涉及两个重要方法:wait()和notify All()
-
第一种解决方案:管程法------使用缓冲池作为容器存放产品
-
第二种解决方案:信号灯法------使用标志位
话不多说上代码
//生产者消费者模型-----线程通信之管程法:使用缓冲区 //模型分析:生产者、消费者、缓冲区、产品 //生产者将产品放入缓冲区后,通知消费者取 //消费者取完产品后,通知生产者生产产品 public class testPC { public static void main(String[] args) { ProContainer container=new ProContainer(); Productor productor=new Productor(container,"母鸡生产鸡场"); Customer customer=new Customer(container,"肯德基"); productor.start(); customer.start(); } } //生产者 class Productor extends Thread{ ProContainer container; public Productor(ProContainer container,String name){ super(name);//传入生产者姓名 this.container=container; } @Override public void run() { super.run(); //生产产品 for (int i = 0; i < 100; i++) { try { container.push(new Chicken(i)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this.getName()+"生产了第"+i+"号鸡"); } } } //消费者 class Customer extends Thread{ ProContainer container; public Customer(ProContainer container,String name){ super(name); this.container=container; } @Override public void run() { super.run(); for (int i = 0; i < 100; i++) { try { Chicken chicken = container.pop(); System.out.println(this.getName()+"拿到了第"+chicken.id+"号鸡"); } catch (InterruptedException e) { e.printStackTrace(); } } } } //产品,假设生产鸡 class Chicken{ int id; public Chicken(int id){ this.id=id; } } //缓冲区 class ProContainer{ //容器大小,缓冲池最多放10个产品 Chicken[]chickens=new Chicken[10]; int count=0;//计数 //生产者生产鸡 public synchronized void push(Chicken chicken) throws InterruptedException { //如果容器已满,生产者等待 if(count==chickens.length){ this.wait(); }//否则就通知消费者,已经做好了一个产品 this.notifyAll(); chickens[count]=chicken; count++; } //消费者消费 public synchronized Chicken pop() throws InterruptedException { //如果没有产品就通知消费者等待 if(count==0){ this.wait(); } //如果容器有产品,就通知生产者已经消费了一个产品 count--; Chicken chicken = chickens[count]; this.notifyAll();//唤醒其他处于等待的线程 return chicken; } }
//生产者消费者模型-----线程通信之信号灯法:使用标志位 public class testPC2 { public static void main(String[] args) { TV tv=new TV("闪光的乐队"); Actor actor=new Actor(tv); Watcher watcher=new Watcher(tv); actor.start(); watcher.start(); } } //生产者--演员 class Actor extends Thread{ TV tv; public Actor(TV tv){ this.tv=tv; } @Override public void run() { for (int i = 0; i < 20; i++) { try { tv.perform(); } catch (InterruptedException e) { e.printStackTrace(); } if(i%2==0){ System.out.println("抖音:记录美好生活");//广告植入 } } } } //消费者--观众 class Watcher extends Thread{ TV tv; public Watcher(TV tv){ this.tv=tv; } @Override public void run() { for (int i = 0; i < 20; i++) { try { tv.wathch(); } catch (InterruptedException e) { e.printStackTrace(); } } } } //产品--电视 class TV{ String performName;//电视名 boolean flag=false;//true:有节目播放,false:没有节目播放 public TV(String performName){ this.performName=performName; } //演员表演节目 public synchronized void perform() throws InterruptedException { //有节目播放,就演员等待 if(!flag){ this.wait(); } //没有节目就通知演员表演节目 this.notifyAll(); System.out.println("表演了"+performName); this.flag=!this.flag; } //观众给观看节目 public synchronized void wathch() throws InterruptedException { //没有节目播放,就观众等待 if(flag){ this.wait(); } //有节目就通知观众观看 this.notifyAll(); System.out.println("播放了"+performName); this.flag=!this.flag; } }
线程池
作用:避免频繁的创建和销毁,实现重复利用,好比共享单车
资源消耗低,提高响应速度,方便线程的管理
通过Executors创建ExecutorService,使用excute方法开启线程
public class testPool { public static void main(String[] args) { //创建线程池---通过Executors创建ExecutorService,使用excute方法开启线程 ExecutorService pool= Executors.newFixedThreadPool(10);//线程池数量 //执行线程 pool.execute(new myTh()); pool.execute(new myTh()); pool.execute(new myTh()); pool.execute(new myTh()); pool.execute(new myTh()); pool.execute(new myTh()); //关闭连接 pool.shutdown(); } } class myTh implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()); } }
这篇关于java多线程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-26Mybatis官方生成器资料详解与应用教程
- 2024-11-26Mybatis一级缓存资料详解与实战教程
- 2024-11-26Mybatis一级缓存资料详解:新手快速入门
- 2024-11-26SpringBoot3+JDK17搭建后端资料详尽教程
- 2024-11-26Springboot单体架构搭建资料:新手入门教程
- 2024-11-26Springboot单体架构搭建资料详解与实战教程
- 2024-11-26Springboot框架资料:新手入门教程
- 2024-11-26Springboot企业级开发资料入门教程
- 2024-11-26SpringBoot企业级开发资料详解与实战教程
- 2024-11-26Springboot微服务资料:新手入门全攻略