初学java 多线程的一些总结

在这里插入图片描述
在这里插入图片描述

public class main {
    
    

    public static void main(String[] args) {
    
    

        yf yjl=new yf();
        //注意:
        //需要调用 start方法开启线程
        //start方法会开启一个新线程,来执行润方法
        //如果直接调用run方法,则方法不会进入就绪态
        yjl.start();
        System.out.println("主线程执行结束");


    }
     static class  yf extends Thread{
    
    

            /*
            从写run方法
            将需要并发执行的方法写在run里
             */
       public void run(){
    
    
           for(int i=1;i<=10;i++)
               System.out.println(i);
       }

    }
}

执行结果
在这里插入图片描述
补充:

1.如果一个start调用了一个已经启动的线程,会抛出illegalthreadstateexception的异常

2.thread类实现runnable接口,其中run()方法正是对runnable接口中的run()方法的具体实现

3.runnable接口的存在是为了类需要继承其他类时(只能继承一个类)也能实现多线程

2.通过Thread实例化对象

public class main {
    
    

    public static void main(String[] args) {
    
    

        Runnable yf=new Runnable() {
    
    
            @Override
            public void run() {
    
    
                for(int i=1;i<=20;i++)
                    System.out.println(i);
            }
        };
        Thread yf1=new Thread(yf);
        yf1.start();
        System.out.println("主线程");


    }
}

在这里插入图片描述

线程的常用方法:
1.线程名字的设置

import javax.lang.model.element.NestingKind;

public class main {
    
    

    public static void main(String[] args) {
    
    
        //方法1
        Thread yf=new Thread();
        yf.setName("lover");
        System.out.println(yf.getName());
        //方法二:应该直接thread一个对象作用不大,故此方法很少用
        Thread yf1=new Thread("yf1");
        System.out.println(yf1.getName());
        //方法3
        Runnable yjl=new Runnable() {
    
    
            @Override
            public void run() {
    
    
                System.out.println("loveyou");
            }
        };
        Thread yf2=new Thread(yjl,"yf2");
        System.out.println(yf2.getName());

        //方法4

        Thread2 yf3=new Thread2("喜洋洋");
        System.out.println();
    }

 

}

class Thread2 extends  Thread{
    
    
    public Thread2 (){
    
    }
    public Thread2(String name){
    
    
        super.setName(name);

        //或 this.setName(name);
    }
}

2.线程的休眠

import javax.lang.model.element.NestingKind;

public class main {
    
    

    public static void main(String[] args) {
    
    
       yf1 yjl1=new yf1("yjl1");
       yf2 yjl2=new yf2("yjl2");


       yjl1.start();
       yjl2.start();




    }



}

 class yf1 extends Thread{
    
    
    yf1(String name){
    
    
        super.setName(name);
    }

    public void run() {
    
    
        for(int i=1;i<=10;i++){
    
    
            System.out.println(getName()+" "+i);
            if(i==5) {
    
    
                try {
    
    
                    this.sleep(1000);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }
    }
}

class yf2 extends Thread{
    
    
    yf2(String name){
    
    
        super.setName(name);
    }

    public void run() {
    
    
        for(int i=1;i<=10;i++){
    
    
            System.out.println(getName()+" "+i);
            if(i==3) {
    
    
                try {
    
    
                    this.sleep(1000);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        }
    }
}

在这里插入图片描述
可以看出 yjl2执行到3后 休眠1秒 yjl1在这段时间应该没人和它抢时间片,所以一直在输出

3.线程的优先级:优先级高的只是具有更大的概率,优先级的设置是一个【0,10】的整数 默认是5

注意:设计线程优先级必须放在该线程执行(start)之前

public class main {
    
    

    public static void main(String[] args) {
    
    
        Runnable yf=new Runnable() {
    
    
            @Override
            public void run() {
    
    
                for(int i=1;i<=10;i++)
                System.out.println(Thread.currentThread().getName()+"  "+i);
            }
        };

        Thread yf1=new Thread(yf,"yf1");
        Thread yf2=new Thread(yf,"yf2");
        Thread yf3=new Thread(yf,"yf3");
        yf3.setPriority(1);
        yf1.setPriority(10);
        yf2.setPriority(5);
        yf1.start();
        yf2.start();
        yf3.start();


    }



}

在这里插入图片描述
可见 优先级高的抢到的几率更大

4.线程的加入:当在执行某个线程A,如果插入线程B,线程A会等待线程B执行完成后执行

import javax.lang.model.element.NestingKind;

public class main {
    
    

    public static void main(String[] args) {
    
    
        Runnable yf=new Runnable() {
    
    
            @Override
            public void run() {
    
    
                for(int i=1;i<=10;i++)
                System.out.println(Thread.currentThread().getName()+"  "+i);
            }
        };

        Thread yf1=new Thread(yf,"yf1");
        Runnable yjl=new Runnable() {
    
    
            @Override
            public void run() {
    
    
                for(int i=1;i<=10;i++) {
    
    
                    System.out.println(Thread.currentThread().getName() + "  " + i);
                      if(i==5) {
    
    
                          try {
    
    
                              yf1.start();
                              yf1.join();
                          } catch (InterruptedException e) {
    
    
                              e.printStackTrace();
                          }
                      }

                }
            }
        };

      Thread  yjl1=new Thread(yjl,"yjl1");
      yjl1.start();


    }



}

在这里插入图片描述
5.线程的中断:以往的时候会用stop来中断线程,但现在已经废弃了,不建议使用stop方法来停止一个线程,现在提倡在run方法中使用无限循环的形式,然后使用一个布尔类型标记控制循环的停止

6.线程的礼让:指的是当前运行状态的内存,去放弃自己的cpu时间片,由运行状态回到就绪状态,假设A和B在同时抢cpu时间片 不一定A礼让后一定执行B,A礼让过后会和B重新争抢,A仍有可能抢到

import javax.lang.model.element.NestingKind;

public class main {
    
    

    public static void main(String[] args) {
    
    
        Runnable yf=new Runnable() {
    
    
            @Override
            public void run() {
    
    
                for(int i=1;i<=10;i++){
    
    
                    System.out.println(Thread.currentThread().getName()+"  "+i);
                    if(i==5)
                        Thread.yield();
                }
            }
        };

        Thread yf1=new Thread(yf,"yf1");
        Thread yf2=new Thread(yf,"yf2");
            yf1.start();
            yf2.start();

    }



}

临界资源问题:被多个线程共享的资源
如下代码,我们模拟了3个人卖苹果

import javax.lang.model.element.NestingKind;

public class main {
    
    
    public static int allapple=200;
    public static void main(String[] args) {
    
    


        Runnable yf=new Runnable() {
    
    
            @Override
            public void run() {
    
    
                while(allapple>0){
    
    
                    allapple--;
                    System.out.println(Thread.currentThread().getName()+"卖出一个苹果剩余苹果数为"+allapple);
                }
            }
        };
        Thread kangkang =new Thread(yf,"kangkang");
        Thread Trump=new Thread(yf,"Trump");
        Thread me=new Thread(yf,"me");
        kangkang.start();
        Trump.start();
        me.start();


    }



}

在这里插入图片描述
从结果可以看出 这个数据是存在问题,这就是临界资源问题(注意 虽然没有任何规律,但每个数只出现了一次);

问题产生的原因:因为我们同时存在3个线程,三个线程在抢cpu,假设kangkang抢到了cpu 执行了allapple-- ,数值才传给输出语句,cpu时间片就被其他的线程抢了,导致输出语句没有执行出来,就是这个原因导致了输出数值的无序性,但每个该出现的数字都出现了

怎么解决这个问题了,我们只需要防止多个线程同时对这个数据操作就行,这里我们引入锁的概念

方法1:同步代码段

import javax.lang.model.element.NestingKind;

public class main {
    
    
    public static int allapple=200;
    public static void main(String[] args) {
    
    


        Runnable yf=new Runnable() {
    
    
            @Override
            public void run() {
    
    
                while(allapple>0){
    
    
                    synchronized (""){
    
    

                        allapple--;
                        System.out.println(Thread.currentThread().getName()+"卖出一个苹果剩余苹果数为"+allapple);
                    }
                }
            }
        };
        Thread kangkang =new Thread(yf,"kangkang");
        Thread Trump=new Thread(yf,"Trump");
        Thread me=new Thread(yf,"me");
        kangkang.start();
        Trump.start();
        me.start();


    }
}

在这里插入图片描述
synchronized是一个同步锁,它修饰的对象有以下几种:

  1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
  2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
  3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
  4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

可以这样理解,凡是被synchronized修饰的 类(…)对其进行操作都需要一把钥匙(钥匙仅有一把),只有抢到了钥匙的线程才能执行

但代码结果还是有问题,为什么了 因为while(allapple>0不在锁类
加个判断就行

import javax.lang.model.element.NestingKind;

public class main {
    
    
    public static int allapple=200;
    public static void main(String[] args) {
    
    


        Runnable yf=new Runnable() {
    
    
            @Override
            public void run() {
    
    
                while(allapple>0){
    
    
                    synchronized (""){
    
    
                         if(allapple<=0)
                            break;
                        allapple--;
                        System.out.println(Thread.currentThread().getName()+"卖出一个苹果剩余苹果数为"+allapple);
                    }
                }
            }
        };
        Thread kangkang =new Thread(yf,"kangkang");
        Thread Trump=new Thread(yf,"Trump");
        Thread me=new Thread(yf,"me");
        kangkang.start();
        Trump.start();
        me.start();


    }
}

显示锁(Reentrantlock):

import javax.lang.model.element.NestingKind;
import java.util.concurrent.locks.ReentrantLock;

public class main {
    
    
    public static int allapple=200;
    public static void main(String[] args) {
    
    
        ReentrantLock lock=new ReentrantLock();

        Runnable yf=new Runnable() {
    
    
            @Override
            public void run() {
    
    
                while(allapple>0){
    
    

                     lock.lock();//上锁
                       if(allapple<=0)
                          break;

                        System.out.println(Thread.currentThread().getName()+"卖出一个苹果剩余苹果数为"+--allapple);
                    lock.unlock();//解锁

                }
            }
        };
        Thread kangkang =new Thread(yf,"kangkang");
        Thread Trump=new Thread(yf,"Trump");
        Thread me=new Thread(yf,"me");

        Trump.start();

        me.start();
        kangkang.start();

    }
}

Reentrantlock和synchronized不同,需要手动上锁手动解锁,但更灵活,我们也可以这样实现一个锁对象,也就是公平锁,说白了就是谁等的时间长谁得到锁
ReentrantLock lock=new ReentrantLock(true);

下面介绍几个方法:
wait方法:等待,是Object类中的一个方法,作用是当前线程释放自己的锁,并且让出cpu时间片,使得当前的线程进去等待队列

notify:是Object类中的一个方法,唤醒等待队列中的一个线程,使这个线程进入锁词

notifyall:唤醒所有的线程

猜你喜欢

转载自blog.csdn.net/jahup/article/details/106043638