线程笔记整理

多线程包括主线程

  • 线程是在进程里面的,一个进程里面有多个线程 包括主线程(main)、gc线程(垃圾回收)是由jvm自带的线程,以及其他线程。

线程创建

  • 方式一

    • 继承Thread类,重写run()方法,调用start开启线程

      package demo01;

      public class Therad1 extends Thread{
         @Override
         public void run() {
             for (int i = 0; i < 2000; i++) {
                 System.out.println("run方法"+i);
            }
        }

         public static void main(String[] args) {
             new Therad1().start();
             for (int i = 0; i < 1000; i++) {
                 System.out.println("main方法"+i);
            }
        }
      }
  • 总结:

    1. 没有主次关系,线程开启不一定立即执行该线程,是由cpu调度的

package demo02;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

public class TestThread2 extends Thread {
   private String url;
   private String name;
//编写构造器
   public TestThread2(String url,String name) {
       this.url = url;
       this.name = name;
  }
//重写run()方法
   @Override
   public void run() {
       Downloader downloader = new Downloader();
       downloader.downloadr(url,name);
       System.out.println("下载了"+name);
  }
//主线程
   public static void main(String[] args) {
       TestThread2 t1 = new TestThread2("http://image.biaobaiju.com/uploads/20180928/16/1538122139-VCTSxjGkvF.jpeg","a.jpeg");
       TestThread2 t2 = new TestThread2("http://image.biaobaiju.com/uploads/20180928/16/1538122139-VCTSxjGkvF.jpeg","b.jpeg");
       TestThread2 t3 = new TestThread2("http://image.biaobaiju.com/uploads/20180928/16/1538122139-VCTSxjGkvF.jpeg","c.jpeg");
       //启动多线程
       t1.start();
       t2.start();
       t3.start();
  }
}
//创建一个下载类
class Downloader {
   //写一个下载方法
   public void downloadr(String url, String name) {
       //处理下载异常
       try {
           FileUtils.copyURLToFile(new URL(url), new File(name));
      } catch (IOException e) {
           //处理异常
           System.out.println("IO异常");
      }
  }
}
  • 方式二

    • 实现runnable接口,重写run()方法,创建new Thread()对象,调用start,把实现接口的对象传进去。

      package demo03;
      //实现接口Runnable
      public class TestThread3 implements Runnable{

         @Override
         public void run() {
             for (int i = 0; i < 20; i++) {
                 System.out.println("run"+i);
            }
        }

         public static void main(String[] args) {
             TestThread3 testThread3 = new TestThread3();
             //创建对象Thread并且把testTherad3传进去
             new Thread(testThread3).start();
             for (int i = 0; i < 2000; i++) {
                 System.out.println("main"+i);
            }
        }
      }
      package demo02;

      import org.apache.commons.io.FileUtils;

      import java.io.File;
      import java.io.IOException;
      import java.net.URL;

      public class TestThread2 implements Runnable {
         private String url;
         private String name;
         public TestThread2(String url,String name){
             this.url=url;
             this.name=name;
        }
         @Override
         public void run() {
             Downloader dl = new Downloader();
             dl.downloader(url,name);
             System.out.println("下载了"+name);
        }

         public static void main(String[] args) {
             TestThread2 t1 = new TestThread2("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586837778&di=32d5ca33cffb54b0607c44a94667c7de&imgtype=jpg&er=1&src=http%3A%2F%2Fa3.att.hudong.com%2F14%2F75%2F01300000164186121366756803686.jpg","1.jpg");
             TestThread2 t2 = new TestThread2("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586837778&di=32d5ca33cffb54b0607c44a94667c7de&imgtype=jpg&er=1&src=http%3A%2F%2Fa3.att.hudong.com%2F14%2F75%2F01300000164186121366756803686.jpg","2.jpg");
             TestThread2 t3 = new TestThread2("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1586837778&di=32d5ca33cffb54b0607c44a94667c7de&imgtype=jpg&er=1&src=http%3A%2F%2Fa3.att.hudong.com%2F14%2F75%2F01300000164186121366756803686.jpg","3.jpg");
             new Thread(t1).start();
             new Thread(t2).start();
             new Thread(t3).start();
        }
      }

      class Downloader{
         public void downloader(String url,String name){
             try {
                 FileUtils.copyURLToFile(new URL(url),new File(name));
            } catch (IOException e) {
                 System.out.println("IO异常");
            }
        }
      }
  • 总结:

    • 第一种方式继承Thread类

    • 子类继承Thread类具备多线程能力

    • 启动线程:子类对象.start()

    • 但是不建议使用:避免OOP单继承局限性

    • 第二种方式实现Runnable接口

    • 实现接口Runnable具有多线程能力

    • 启动线程:传入目标对象+Thread对象.start()

    • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

并发问题

package demo03;

public class TestThread4 implements Runnable{
   //多个线程操作同一个数据时,会出现数据紊乱,线程不安全
   private int trickNums=10;
   @Override
   public void run() {
       while (true){
           if (trickNums<=0){
               break;
          }
           System.out.println(Thread.currentThread().getName()+"拿到了"+trickNums--+"张票");
           try {
               Thread.sleep(200);
          } catch (InterruptedException e) {

               System.out.println("睡眠异常");
          }
      }
  }

   public static void main(String[] args) {
       TestThread4 testThread4 = new TestThread4();
       new Thread(testThread4,"小明").start();
       new Thread(testThread4,"张三").start();
       new Thread(testThread4,"李四").start();
       new Thread(testThread4,"王五").start();
  }
}

Lamda表达式

  • 函数式接口的定义:

    • 任何接口,如果里面只有一个抽象方法,那么他就是一个函数式接口

      public interface Runnable {
         public abstract void run();
      }
  • package demo03;

    public class TestLamda {

       //2.static内部类
     /*static   class Student implements Teacher{
           @Override
           public void love(int a) {
               System.out.println("Hello"+a);
           }
       }*/
       public static void main(String[] args) {
    Teacher teacher=null;
           //3.局部内部类
    /*       class Student implements Teacher{
               @Override
               public void love(int a) {
                   System.out.println("Hello"+a);
               }
           }*/
           //匿名内部类,没有类的名称,必须借助接口和父类
    /*       new Teacher() {
               @Override
               public void love(int a) {
                   System.out.println("Hello"+a);
               }
           }.love(3);*/
    //lamada简化
          teacher=(a,b)-> {System.out.println("Hello"+(a+b)); };
          teacher.love(4,9.8);
      }
    }

    //创建接口:创建一个函数式接口,接口里面只能有一个抽象方法
    interface Teacher {
       void love(int a,double b);
    }
    //1.创建实现类接口
    /*
    class Student implements Teacher{

       @Override
       public void love(int a) {
           System.out.println("Hello"+a);
       }
    }*/

停止线程

  • new --->就绪状态--->运行状态---->阻塞状态或者dead状态

  • 测试stop:

    1. 建议线程正常停止--->利用次数,不建议死循环

    2. 建议使用标志位--->设置一个标志位

    3. 不要使用stop或者destroy等过时或者jdk不建议使用的方法

    4. 我们一般建议使用一个标志位终止变量当flag为false时,停止该线程运行

    package demo03;

    public class TestStop implements Runnable {
       //定义一个线程标识
       private boolean flag=true;
       //使用该线程标识
       @Override
       public void run() {
           int i=0;
           while (flag){
               System.out.println("run...thread"+i++);
          }
      }
    //写一个结束线程的stop方法
       public void stop(){
           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==90){
                   testStop.stop();
                   System.out.println("线程停止");
              }

          }
      }
    }

线程休眠

  • sleep指定当前线程阻塞的毫秒数

  • sleep存在异常

  • sleep时间到达后线程进入就绪状态

  • sleep可以模拟网络延时,倒计时等

  • 每一个对象都有一个锁,sleep不会释放锁

public static void main(String[] args) {
    //模拟网络延时
    Date date = new Date(System.currentTimeMillis());
    while (true){
        try {
            Thread.sleep(1000);
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
            date = new Date(System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
package demo03;

//模拟倒计时
public class TestSleep {

    public static void main(String[] args) {
        try {
            tenDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

   public static void tenDown() throws InterruptedException {
        int i = 10;
        while (true) {
            Thread.sleep(1000);
            System.out.println(i--);
            if (i <= 0) {
                break;
            }
        }

    }
}

线程礼让

  • 让正在执行的线程暂停,但不阻塞

  • 将线程从运行状态转为就绪状态

  • 让cpu重新调度,礼让不一定成功,看cpu心情

    package demo03;
    
    public class TestYield {
        public static void main(String[] args) {
            MyYield myYield = new MyYield();
            new Thread(myYield,"a").start();
            new Thread(myYield,"b").start();
        }
    }
    class MyYield implements Runnable{
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"开始执行");
            Thread.yield();
            System.out.println(Thread.currentThread().getName()+"结束执行");
        }
    }

线程强制执行

  • Join合并线程,待此线程完成后,在执行其他线程,其他线程阻塞

  • 可以想成插队

    package demo03;
    
    public class TestJoin implements Runnable {
        @Override
        public void run() {
            for (int i = 1; i <= 200; i++) {
                System.out.println("vip"+i);
            }
        }
    
        public static void main(String[] args) {
            //线程启动
            TestJoin testJoin = new TestJoin();
            Thread thread = new Thread(testJoin);
            thread.start();
    
    
            //主线程
            for (int i = 1; i <= 500; i++) {
    
                if (i == 100) {
                    try {
                        thread.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("main"+i);
            }
        }
    }
  • 线程对象一旦创建,就进入到了新生状态

  • 当调用start()方法,线程立即进入就绪状态,但并不意味着立即调度执行

  • 进入运行状态,线程才真正执行线程体的代码块

  • 当调用sleep、wait或同步锁定时,线程进入阻塞状态,就是代码不往下执行,阻塞事件解除后,重新激怒人就绪状态,等待cpu调度执行

  • 线程中断或者结束,一旦进入死亡状态,就不能再次启动

线程的优先级

  • 范围1~10

  • min=1;最小

  • max=10 最大

  • main方法默认5

  • 使用以下方式改变或获取优先级

    • getPrioity.setPrioity(int xxx);

  • 优先级高的不一定一定会先执行,只是先执行的概率更大了 ,都看cpu的调度

  • 先设置优先级,在启动线程

    package demo03;
    
    public class TestPrioity {
        public static void main(String[] args) {
            System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
            MyPrioity myPrioity = new MyPrioity();
            Thread t1 = new Thread(myPrioity,"线程一");
            Thread t2 = new Thread(myPrioity,"线程二");
            Thread t3 = new Thread(myPrioity,"线程三");
            Thread t4 = new Thread(myPrioity,"线程四");
            Thread t5 = new Thread(myPrioity,"线程五");
            Thread t6 = new Thread(myPrioity,"线程六");
    
    
            t6.setPriority(8);
            t6.start();
    
            t1.setPriority(1);
            t1.start();
    
            t2.setPriority(Thread.MAX_PRIORITY);
            t2.start();
    
    /*
    //优先级越界
            t3.setPriority(11);
            t3.start();
    //优先级越界
            t4.setPriority(-1);
            t4.start();
    */
    //最大优先级
            t5.setPriority(Thread.MIN_PRIORITY);
            t5.start();
    
    
        }
    }
    class MyPrioity implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
        }
    }

守护进程

  • 线程分为用户线程和守护进程

  • 虚拟机必须确保用户线程执行完毕

  • 虚拟机不用等待守护进程执行完毕

  • 如:后台记录操作日志,监控内存,垃圾回收等待

    package demo03;
    
    public class TestDaemon {
        public static void main(String[] args) {
            God god = new God();
            You you = new You();
    
    
            Thread thread = new Thread(god);
            //把上帝变成守护进程
            thread.setDaemon(true);
            thread.start();
    
    
            new Thread(you).start();
        }
    }
    class You implements  Runnable{
    
        @Override
        public void run() {
            for (int i = 0; i < 300; i++) {
                System.out.println("人生不过36500天"+i);
            }
    
        }
    }
    class God implements  Runnable{
    
        @Override
        public void run() {
            while (true){
                System.out.println("上帝一直守护着你");
            }
    
        }
    }

线程同步

  • 并发:同一个对象被多个线程同时操作

  • 由于同一个线程同享一快存储空间,在带来方便的同时,,也会带访问冲突问题,为了保证数据在方法中被访问时的正确性,,在访问时加入锁机制synchronized,当一个线程 获得对象的排他锁,独占资源,其他线程必须等待,使用后释放锁 即可,会引起性能问题,如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题

Lock

  • lock是显示锁(手动开启和手动关闭,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放,

  • 使用lock锁性能更好

package demo03;

public class TestLock {
    public static void main(String[] args) {
        MyLock myLock = new MyLock();
        new Thread(myLock,"小黄").start();
        new Thread(myLock,"小白").start();
        new Thread(myLock,"小黑").start();

    }
}
class MyLock implements Runnable{
    int trickNums=10;
    @Override
    //并发不安全我们可以用synchronized给方法加锁
    public synchronized void run() {
        while (true){
            if (trickNums>0){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"抢到了第"+trickNums--);
            }else {
                break;
            }
        }
    }
}
package demo03;

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    public static void main(String[] args) {
        MyLock myLock = new MyLock();
        
        new Thread(myLock,"小白").start();
        new Thread(myLock,"小黑").start();
    }
}
class MyLock implements Runnable{
    //定义lock锁
  ReentrantLock  lock=new ReentrantLock();
    int trickNums=10;
    @Override
    public  void run() {

            while (true){
                try {
                    //上锁
                    lock.lock();
                    if (trickNums>0){
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+"抢到了第"+trickNums--);
                    }else {
                        break;
                    }
                } finally {
                    //解开锁
                    lock.unlock();
                }
            }
        }
    }
posted @ 2020-04-10 22:09  Crazylearningjava  阅读(404)  评论(0)    收藏  举报