JAVA多线程总结01

 

一:概述:

  1.1什么是进程,什么是线程:

    进程是一个应用程序,线程是一个应用单元。eg:现在java的dos命令里面最起码有main线程和垃圾回收线程

  1.2进程和线程的关系:

    进程A和进程B内存空间独立不共享,线程A和线程B堆内存和方法区内存共存,但是栈内存,一个线程一个栈。(堆和方法区共享,栈内存独立)

 

二:实现线程的方法:

  第一种:编写一个新类,直接继承java.lang.thread;并且重写run方法

 

public class Yunxing2 {
    public static void main(String[] args) {
    MyThread myThread = new MyThread();//新建线程
    myThread.start();//启动新线程
    //作用:启动一个分支线程,在JVM中开辟一个新的栈空间,任务完成后,瞬间就结束了
    //启动成功后,自动调用run方法,并且run方法跟main方法一样在栈空间的底部,同级别的
    //这里的代码还是运行在主线中
    myThread.run();//不用这个
    //作用L不会启动线程,不会分配新的分支栈中,是单线程的run()方法
        for(int i=0;i<1000;i++){
            System.out.println("逐鹿线---->"+i);
        }
    }
}
//继承+重写run方法,main中new出一个该类,然后使用start启动一个新的线程
class MyThread extends Thread{//定义线程类
    @Override
    public void run() {
        //编写程序,这段程序运行在分支线程中(分支)
        for(int i=0;i<1000;i++) {
            System.out.println("分支线程---->"+i);
        }
    }
}

 

 

第二种方法:编写一个类,实现java.lang.Runnable 接口(这种方法更好,因为一个类实现了一个接口,还可以继承别的类)

//定义一个可运行的类
public class MyRunnable implements Runnable{
    public void run();
}
//创建一个线程对象Thread,并且传一个runnable参数
Thread t= new Thread(new MyRunnable());
//启动线程
t.start();

 

 

第三种方法:匿名内部类直接编写分支线程代码:

//main函数中直接写就好
Thread t=new Thread(new Runnable(){//在这里直接实现,这就是匿名内部类
    @Override
    public void run(){
        for(int i=0;i<100;i++){
            System.out.println("t线程---->"+i);
        }
    }
});
t.start();//启动支线线程
for(int i=0;i<100;i++){
    System.out.printn("main线程--->"+i);
}

 

 

三:线程生命周期:

新建状态->就绪状态->运行状态->阻塞状态->死亡状态

 

 

四:线程的一些操作:

  1. 获取并且修改线程的名字

    //用第一种创造线程的方式:
    MyThread t= new MyThread();
    MyThread.setname("新线程");
    String tname=MyThread.getname();

     

  2. 获取当前线程对象:(有点类似于this指针)

    //获取:static Thread currentThread();
    //调用:Thread Thread.currrentThread();
    Thread t=Thread.currentThread();
    System.out.println(t.getname+"--->"+123123);
    //如果这段代码出现在主线程中,当前线程就是主线程,
    //当t1线程执行该代码,就是t1线程
    //当t2线程执行该代码,就是t2线程

     

  3. 线程Sleep方法:

    /*
        static void sleep(long millis);
        1.静态方法:Thread.sleep(1000);
        2.参数:毫秒
        3.作用:让当前线程进入休眠《进入“阻塞状态”,放弃占用CPU时间片
        4.Thread.sleep();可以像计时器那样,间隔记录时间
    */
    Thread t=new MyThread();
    t.start();
    try{
        t.sleep(millis:1000*5);
    }catch(InterruptedException e){
        e.printStaclTrace();
    }

     

  4. 中断睡眠方式:interrupt,但是这种终断睡眠的方式是依靠java的异常处理机制实现的

    public static void main(String[] args) {
        Thread t=new Thread(new MyRunnable2());
        t.setName("t");
        t.start();
        //必须要有try,catch异常机制
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        t.interrupt();//这里中断支线程的sleep睡眠
    }
    //这个放在外面就好了,不要放在包创建的class里面
    class MyRunnable2 implements Runnable{
    ​
        @Override
        public void run() {
            // TODO Auto-generated method stub
            try {
                Thread.sleep(1000*60*60*30);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
    }

     

  5. 结束线程:

    //直接杀死线程,直接干掉这个线程(已过时,容易丢是数据)
    跟上面的终止一样,就是最后哪里变成
    t.stop();
    //常用的结束线程,一般就是打一个bollean b标记,如果有问题,将这个变为false,想什么时候结束线程,将该类中b的值改为false,最后来一个return ;

     

  6. 线程调度:

    1. 实例方法:

      void setPriority(int newPriority);
      int getPriority()//获取线程有优先级
      //最低优先级:1,最高优先级:10,默认优先级:5
          
      //设置优先级:
      Thread.currentThread().setPriority(1);

       

    2. 静态方法:

      static void yield()//让位方法
      //暂停当前正在执行的线程对象,yield()方法不是阻塞方法,让当前线程让位,让给其他线程使用
      //yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”
      //注意:回到“就绪状态”还有可能再次强到。
          
      //设置让位:
          class MyRunnable6 implements Runnable{
              @Override
              public void run(){
                  for(int i=1;i<10000;i++){
                      //每100个让位一次
                      if(i%100==0){
                          Thread.yield();//当前线程让位
                      }
                      System.out.println(Thread.currentThread().getName()+"---->"+i);
                  }
              }
          }

       

    3. 线程合并join:同样的,join方法也是需要java异常机制的执行才可以

      void join()
      //合并线程
      class MyThread1 extends Thread{
          public void doSome(){
              Mythread2 t=new MyThread2();
              t.join();//当前线程受阻,t线程执行
          }
      }

       

  7. 多线程并发环境下,数据安全问题

    1. 重点:以后写代码需要在多线程并发的环境下考虑数据安全的问题

    2. 线程不安全的条件:满足下面三个条件就会有线程安全问题

      1. 多线程并发

      2. 有共享数据

      3. 共享数据有修改的行为

    3. 如何解决线程安全问题:使用线程同步机制,但是会损失一些效率,莫得办法

    4. 哪些数据有数据安全问题:实例变量和静态变量,局部变量没有安全问题

    5. 举例:编写两个线程对同一个账号读取并录入信息使用同步代码快synchronized

  8. 线程同步机制:synchronized

    1. 语法:一般也可以直接在构造方法的时候加入,这样就对类中其中的一种方法加上一个锁

      synchronized(obj/this){
          //线程同步代码快
                  //取款之前的余额
              double before=this.getBalancce();
              //取款之后的余额
              double after=before-money;
              //读取剩下的余额的信息
              this.getBalancce();
      }

      synchronized后面的括号中传的这数据时相当关键的,
      这个数据必须时多线程共享的数据,才能达到多线程排队
      写什么?
         那要看什么线程的同步,或者说填写共享对象
         填写this或者你想同步的那几个线程的共享对象
    2. 原理:java语言当中,每个对象都有一把锁,也就相当于是一个标记,而使用synchronized传递对象的时候,只能同时占有一把锁,所以其他线程只能等候占用锁来继续执行下面的代码。

    3. 三种写法

      第一种:同步代码块:
         灵活
         synchronized(线程共享对象){
         t同步代码快
      }

      第二种:在实例方法中使用synchronized
         表示共享对象一定时this
         并且同步代码快时整个方法体
         
      第三种:在静态方法上使用synchronized,保证静态变量的安全
         表示找类锁
         类锁永远只有一把
         就算创建了100个对象,类锁也只有1个
      public class Exam01{
          public static void  main(String[] args){
              Myclass mc=new Myclass();
              Thread t1=new MyThread(mc);
              Thread t2=new MyThread(mc);
              t1.setName("t1");
              t2.setName("t2");
              
              t1.start();
              Thread.sleep(1000);
              t2.start();
          }
      }
      ​
      class MyThread extends Thread{
          private Myclass=mc;
          public void run(){
              if(Thread.currentThread().getName().equals("t1")){
                  mc.dosome();
              }
              if(Thread.currentThread().getName().equals("t2")){
                  mc.doother();
              }
          }
      }
      ​
      class Myclass{
          public synchronized void dosome(){//这是对象锁,如果前面加一个static,那就是类锁,不管创建了几个对象,都需要等待上一个线程执行完成,才能执行下一个步骤
              System.out.println("dosome begin");
              try{
                  Thread.sleep(1000*10);
              }catch(InterruptedException e){
                  e.printStackTrace();
              }
              System.out.println("dosome over");
              public void doother(){
                  System.println("doother begin");
                  System.println("doother over");
              }
          }
      }
      ​
      ​
      //注意:该线程使用类锁时,dosome有synchronized,但是doother没有synchronized,所以没有排序,两个异步处理,并没有同步,所以不安全
      只有当两个全都有synchronized的时候才会有类锁性质顺序

       

       
  9. 死锁:直接上手,需要会写死锁

    //一般是使用synchronized的时候,使用了嵌套的方法,可能会死锁,若是在死锁途中还进行睡眠操作,那将会导致程序停留崩溃

    class MyThread1 extends Thread{
        Obiect o1;
        Object o2;    
        
        public void run(){
            synchronized(o1){
                synchronized(o2){
                    
                }
            }
        }
    } 
    ​
    class MyThread2 extends Thread{
        Obiect o1;
        Object o2;    
        
        public void run(){//这里的两个object位置互换了一下
            synchronized(o2){
                synchronized(o1){
                    
                }
            }
        }
    } 
    ​
    //以上代码,在main类中同步调用的时候,也就是MyThread1.start();MyThread2.start();会出现死锁现象,导致程序暂停。
     
  10. 如何解决线程安全问题?(这个之后再说吧)

    1. 创建局部变量,没有安全问题
    2. 创建多个实例对象,实例变量的内存就不共享了
    3. 啥都不能用的话,只能适合用synchronized了

 

 

posted @ 2020-09-05 18:08  焕不涣  阅读(131)  评论(0编辑  收藏  举报