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多线程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程