Java 多线程

Java 多线程

  • 在程序运行 主线程,为系统的入口,用于执行整个程序;

  • 在一个进程中,如果开辟了多个线程,线程的运行有调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的;

  • 对同一份资源操作时,会财富商城存在资源抢夺的问题,需要加入并发控制;

  • 线程会带来额外的开销,如cpu调度时间,并发控制开销;

  • 每个线程在自己的工作内存交互,没存控制不当会造成数据不一致

//创建多线程的方法一,继承类法
public class TestThread1 extends Thread{
   //继承多线程类后,一定要重写run()方法;
   @Override
   public void run() {
       for (int i = 0; i < 100000; i++) {
           System.out.println("正在吃饭"+i);
      }

  }

   public static void main(String[] args) {
       //如何调用线程,新建线程再调用多线程的方法
       TestThread1 testThread1 = new TestThread1();
       //调用start()方法,开启线程,同时运行两线程
       testThread1.start();
       //两线程的运行,看cpu调度安排,每次运行结果不一定相同
       for (int i = 0; i < 10000; i++) {
           System.out.println("正在玩手机"+i);
      }
  }
}

1、网络图片下载

  • 下载commons-io第三方包,导入包

  • 创建类,调用commons-io方法(FileUtils.copyURLToFile(new URL(url),new File(name));),记得捕获异常

  • 创建线程类,重写run()方法,调用下载图片方法,新建构造函数给新建对象赋值;

  • 新建主函数,新建线程对象,调用线程方法start();

//熟悉线程使用,用线程类的经行多进程图片下载
import org.apache.commons.io.FileUtils;

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

public class TestThread2 extends Thread{
   public String url;
   public String name;

   public TestThread2(String url,String name){
       this.name = name;
       this.url  = url;
  }
   @Override
   public void run() {
       WebDownloader webDownloader = new WebDownloader();
       webDownloader.downloader(url,name);
       System.out.println("下载好了"+name);
  }

   public static void main(String[] args) {
       TestThread2 t1 = new TestThread2("https://desk-fd.zol-img.com.cn/t_s640x530c5/g2/M00/04/0B/ChMlWl6MB12IVbpeACLVCtPy50YAAOIDgLYHRYAItUi612.jpg","picture1.jpg");
       TestThread2 t2 = new TestThread2("https://desk-fd.zol-img.com.cn/t_s960x600c5/g2/M00/04/0B/ChMlWl6MB0KISfTsAARAHsNNgtAAAOIDQA0uYcABEA2297.jpg","picture2.jpg");
       TestThread2 t3 = new TestThread2("https://desk-fd.zol-img.com.cn/t_s960x600c5/g2/M00/04/0B/ChMlWl6MB0iIE2g5ACfFb_zOgdEAAOIDQE-5eIAJ8WH455.jpg","picture3.jpg");
       t1.start();
       t2.start();
       t3.start();
  }


   class WebDownloader{
       public void downloader(String url,String name){
           try {
               FileUtils.copyURLToFile(new URL(url),new File(name));
          } catch (IOException e) {
               e.printStackTrace();
               System.out.println("IO异常,downloader方法出现问题");
          }
      }
  }

}

2、Runnable接口线程

  • Runnable接口对比Thread类,避免了单继承的局限性,灵活方便

  • 创建类TestThread3,继承Runnable,重写run()方法;

  • 新建线程对象testThread3,再新建线程对象thread,把对象testThread3丢到thread里面才能运行

  • 调用thread调用start()方法运行

public class TestThread3 implements Runnable{
   @Override
   public void run() {
       for (int i = 0; i < 500; i++) {
           System.out.println("多线程跑起来"+i);
      }

  }

   public static void main(String[] args) {
       TestThread3 testThread3 = new TestThread3();
       //都是这样使用的,需要把目标放到Thread()里面运行
       //接口类线程开始的简写
       new Thread(testThread3).start();//
       //完全式的接口线程开始
       Thread thread = new Thread(testThread3);//这一步是死规定
       thread.start();

       for (int i = 0; i < 200; i++) {
           System.out.println("主线程跑起来"+i);
      }
  }
}
//Runnable方法实现多线程图片下载
import org.apache.commons.io.FileUtils;

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

public class TestThread2 implements Runnable{
   public String url;
   public String name;

   public TestThread2(String url,String name){
       this.name = name;
       this.url  = url;
  }
   @Override
   public void run() {
       WebDownloader webDownloader = new WebDownloader();
       webDownloader.downloader(url,name);
       System.out.println("下载好了"+name);
  }

   public static void main(String[] args) {
       TestThread2 t1 = new TestThread2("https://desk-fd.zol-img.com.cn/t_s640x530c5/g2/M00/04/0B/ChMlWl6MB12IVbpeACLVCtPy50YAAOIDgLYHRYAItUi612.jpg","picture1.jpg");
       TestThread2 t2 = new TestThread2("https://desk-fd.zol-img.com.cn/t_s960x600c5/g2/M00/04/0B/ChMlWl6MB0KISfTsAARAHsNNgtAAAOIDQA0uYcABEA2297.jpg","picture2.jpg");
       TestThread2 t3 = new TestThread2("https://desk-fd.zol-img.com.cn/t_s960x600c5/g2/M00/04/0B/ChMlWl6MB0iIE2g5ACfFb_zOgdEAAOIDQE-5eIAJ8WH455.jpg","picture3.jpg");
       new Thread(t1).start();
       new Thread(t2).start();
       new Thread(t3).start();
  }


   class WebDownloader{
       public void downloader(String url,String name){
           try {
               FileUtils.copyURLToFile(new URL(url),new File(name));
          } catch (IOException e) {
               e.printStackTrace();
               System.out.println("IO异常,downloader方法出现问题");
          }
      }
  }

}

3、初识并发问题

  • 多个线程调用同一个对象

  • 有可能会出现调用资源错误,多个线程调用同一个资源

//程序还有问题,因为出现不同人抢到同一张票
public class TestThread4 implements Runnable{
   private int number = 10;
   @Override
   public void run() {
       while (true){
           if(number<=0){
               break;
          }
           try {
               Thread.sleep(200);
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
           //获取当线程的名字
           System.out.println(Thread.currentThread().getName()+"抢到了第"+number--+"票");
      }
  }

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

4、龟兔赛跑问题(待解决问题:乌龟赢后,兔子还会跑一次)

  1. 首先来个赛道距离,然后要离终点越来越近;

  2. 判断比赛是否结束;

  3. 打印出胜利者;

  4. 龟兔赛跑开始;

  5. 故事中是乌龟赢,兔子需要睡觉,所以我们需要模拟兔子睡觉;

  6. 终于,乌龟赢得比赛

//兔子乌龟赛跑比赛
public class Race implements Runnable {
   private static String winner;

   @Override
   public void run() {

       //第一步规定赛道是1000,兔子乌龟跑了多少步
       for (int i = 0; i <= 1000; i++) {
           //兔子睡觉
           if(Thread.currentThread().getName().equals("兔子")){
               try {
                   Thread.sleep(0);
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
           //判断是否有人获得冠军
           boolean flag = gameOver(i);
           if(flag){
               break;
          }
           System.out.println(Thread.currentThread().getName() + "跑了==" + i + "==步");
      }


  }

   //判断比赛是否结束,谁是冠军
   private boolean gameOver(int steps) {
       //判断是否有胜利者
       if (winner != null) {
           return true;//有人就结束了
      } else {
           if (steps == 1000) {
               winner = Thread.currentThread().getName();
               System.out.println(winner + "获得冠军");
               return true;
          }

      }
       return false;
  }
   public static void main (String[] args){
       Race race = new Race();
       //
       new Thread(race, "兔子").start();
       new Thread(race, "乌龟").start();
  }

}

5、静态代理模式(多线程的底层实现模拟)

如何创建代理模式:1、创建接口类;2、创建代理类和雇主类都要使用接口类和重写接口类的方法;3、代理类里面需要创建调用雇主类的目标,创建构造函数调用雇主类目标,实现方法需要调用雇主目标,还可以创建更多的方法执行雇主类不能实现的其他功能;4、创建主函数,创建代理对象,代理对象调用雇主目标,实现方法。

  • 真时对象和代理对象都要同时实现统一接口

  • 代理对象要代理真实对象

  • 代理对象可以帮助真实对象做好多他们做不了的事情

  • 真实的对象只要专注于做自己的事情就好了

//静态代理模式总结
//真时对象和代理对象都要同时实现统一接口
//代理对象要代理真是对象
//好处
  //代理对象可以帮助真实对象做好多他们做不了的事情
  //真实的对象只要专注于做自己的事情就好了
public class StaticProxy {
   public static void main(String[] args) {
      You  you = new You();//创建结婚对象
      new Thread( ()-> System.out.println("成功了") ).start();//Lambda表达式
      new WeddingCompany(you).HappyMarry();;

  }
}

interface Marry{
   void HappyMarry();
}
//真时角色
class You implements Marry{
   @Override
   public void HappyMarry() {
       System.out.println("张三要结婚了,很开心");
  }
}
//代理公司,代理真实角色结婚
class WeddingCompany implements Marry{
   //
   private Marry target;

   public WeddingCompany(Marry target) {
       this.target = target;
  }

   @Override
   public void HappyMarry() {
       befor();
       this.target.HappyMarry();
       after();
  }

   private void after() {
       System.out.println("结婚之后,付尾款");
  }

   private void befor() {
       System.out.println("结婚之前,紧张筹备");
  }

}

6、线程停止

  • 建议正常停止

  • 设置标志位,正常停止线程

  • 不建议使用stop和distroy等过时方法或其他JDK不推荐的方法

/*
1、创建类,继承Runnable接口,设置一个类变量flag,重写run方法
2、创建一个停止方法stop,更改flag的状态
3、创建主方法,新建对象,调用start方法,创建循环,循环调用stop方法,停止线程
*/
public class ThreadStop implements Runnable{
   private boolean flag = true;
   @Override
   public void run() {
       int i = 0;
       while (flag){
           System.out.println("线程运行中"+i++);
      }
  }

   public void stop (){
       this.flag = false;
  }

   public static void main(String[] args) {
       ThreadStop threadStop = new ThreadStop();

       new Thread(threadStop).start();

       for (int i = 0; i <100000 ; i++) {
           if(i==90000){
               threadStop.stop();
               System.out.println("线程停止运行");
          }
      }
  }
}

7、线程休眠sleep

  • 倒计时10秒

  • 打印当前时间

  • 使用sleep记得捕获异常就好

//倒计时10秒
import static java.lang.Thread.sleep;
public class ThreadSleep {
    
    public static void main(String[]args)  {
        try {
            new ThreadSleep().tenDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void tenDown() throws InterruptedException {
        int num = 10;
        while(true){
            System.out.println(num--);
            Thread.sleep(1000);
            if(num<=0){
                break;
            }
        }
    }
}
//打印当前时间
import java.text.SimpleDateFormat;
import java.util.Date;
import static java.lang.Thread.sleep;
public class ThreadSleep {
    public static void main(String[]args)  {
        Date startTime = new Date(System.currentTimeMillis());
        while (true){
            try {
                Thread.sleep(1000);//休眠一面
                //把系统时间转为简单的格式打印
                System.out.println(new SimpleDateFormat("hh:mm:ss").format(startTime));
                startTime = new Date(System.currentTimeMillis());//更新startTime里面的时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

8、线程礼让yield

  • 让当前线程暂停,但不阻塞

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

  • 让cpu重新调度,礼让不一定成功,让不让cpu说了算

public class ThreadYield {
    public static void main(String[] args) {
        //正常运行,礼让成功就是先运行a,再运行b。如不成功就a停止再运行b
        new Thread(new MyYield(),"a").start();
        new Thread(new 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()+"暂停运行");
    }
}

9、线程插队-Join

  • 让当前线程暂停且阻塞

  • 插队线程运行结束其他线程才能继续运行

public class ThreadJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("VIP来插队了"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadJoin threadJoin = new ThreadJoin();
        Thread thread = new Thread(threadJoin);//插队一定要这样创建对象,
        thread.start();//静态代理方法,线程开始运行
       
        for (int i = 0; i < 500; i++) {
            if(i==200){
                thread.join();//强行插队,并阻塞主线程,该线程运行完主线程才能继续运行
            }
            System.out.println("主线程运行"+i);
        }
    }
}

10、线程优先级

  • 优先级最高为10,最低为1,主函数线程优先级一般为5

  • 并不是优先级高就最先执行,还是要看CPU的心情

//优先级最高为10,最低为1,主函数线程优先级一般为5
//并不是优先级高就最先执行,还是要看cpu的心情
public class ThreadPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()
                +"优先级为:"+Thread.currentThread().getPriority());
    }

    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()
                +"优先级为:"+Thread.currentThread().getPriority());
        ThreadPriority threadPriority = new ThreadPriority();
        Thread thread1 = new Thread(threadPriority,"线程一");
        Thread thread2 = new Thread(threadPriority,"线程二");
        Thread thread3 = new Thread(threadPriority,"线程三");
        Thread thread4 = new Thread(threadPriority,"线程四");
        Thread thread5 = new Thread(threadPriority,"线程五");
        thread1.start();

        thread2.setPriority(Thread.MIN_PRIORITY);
        thread2.start();

        thread3.setPriority(Thread.MAX_PRIORITY);
        thread3.start();

        thread4.setPriority(5);
        thread4.start();
    }
}

11、线程守护

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

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

  • 虚拟机不用等待守护线程执行完毕,如后台记录操作日志,监控内存,垃圾回收等处理

public class ThreadGuard {
    public static void main(String[] args) {
        God god = new God();
        Human human = new Human();
        Thread thread = new Thread(god);
        thread.setDaemon(true);//----重点----默认false是用户线程,改为true是守护线程
        thread.start();//守护线程启动
        new Thread(human).start();//用户线程启动

    }
}

class God implements Runnable{

    @Override
    public void run() {
        while(true){
            System.out.println("---------God is guarding------------");
        }
    }
}

class Human implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("---------Humans is living------------");
        }
        System.out.println("------------Say Goodbye-------------");
    }
}

12、线程同步机制

  • 让当前线程暂停,但不阻塞

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

  • 让cpu重新调度,礼让不一定成功,让不让cpu说了算

public class ThreadYield {
    public static void main(String[] args) {
        //正常运行,礼让成功就是先运行a,再运行b。如不成功就a停止再运行b
        new Thread(new MyYield(),"a").start();
        new Thread(new 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()+"暂停运行");
    }
}

13、线程同步,安全监控

  • 同步块:synchronized(obj){},

  • obj可以是任何对象,建议是需要增删改的对象,才能避免数据混乱

  • 同步块的作用是:限制访问该对象,即由线程访问该对象后立即锁住该对象,等访问结束后才会解锁对象给其他线程访问,即控制对象只能给一个线程访问处理。

import java.util.ArrayList;

public class UnsafeList {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                synchronized (list){
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        try {
            Thread.sleep(100);//有了整个延迟才能锁住,没延迟锁不住啊
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}
//安全的集合CopyOnWriteArrayList,不用锁也可以正常运行?
import java.util.concurrent.CopyOnWriteArrayList;

public class ThreadJUC {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

 

14、死锁和死锁避免方法

  • 互斥条件:一个资源每次只能被一个进程调用;

  • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放,请求另外一个资源前,把手里的资源释放

  • 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺;

  • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

public class DeadLock {
    public static void main(String[] args) {
        Makeup lucy = new Makeup(0, "Lucy");
        Makeup mari = new Makeup(1, "Mari");
        lucy.start();
        mari.start();
    }
}

class Mirror{

}
class Lipstick{

}
class Makeup extends Thread{
    final static Mirror mirror = new Mirror();//用静态类型,保证程序执行过程中该对象唯一
    final static Lipstick lipstick = new Lipstick();
    int choice;
    String girlName;
    Makeup(int choice,String girlName) {//构造函数,创建对象时赋予类初始值
       this.choice = choice;
       this.girlName = girlName;
    }
      @Override
    public void run() {
          try {
              makeup();//一定放在run方法里面,makeup()方法不运行
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      }

    private void makeup() throws InterruptedException {
        if(choice==0){
            synchronized (lipstick){//注意锁里面不能再放锁,一层层锁容易出错,混乱
                System.out.println("========口红正在被使用0=======");
                Thread.sleep(1000);//被占用1秒
            }
            synchronized (mirror){
                System.out.println("========镜子正在被使用0=======");
            }
        } else{
            synchronized (mirror){
                System.out.println("========镜子正在被使用1=======");
                Thread.sleep(2000);//被占用2秒
            }
            synchronized (lipstick){
                System.out.println("========口红正在被使用1=======");
            }
        }
    }

}

15、Lock锁

  • 可重入锁:ReentrantLock

  •  

  •  

public class ThreadYield {
    public static void main(String[] args) {
        //正常运行,礼让成功就是先运行a,再运行b。如不成功就a停止再运行b
        new Thread(new MyYield(),"a").start();
        new Thread(new 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()+"暂停运行");
    }
}

16、管程法

  • 可重入锁:ReentrantLock

  •  

  •  

public class ThreadYield {
    public static void main(String[] args) {
        //正常运行,礼让成功就是先运行a,再运行b。如不成功就a停止再运行b
        new Thread(new MyYield(),"a").start();
        new Thread(new 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()+"暂停运行");
    }
}

17、信号灯法

  • 可重入锁:ReentrantLock

  •  

  •  

public class ThreadYield {
    public static void main(String[] args) {
        //正常运行,礼让成功就是先运行a,再运行b。如不成功就a停止再运行b
        new Thread(new MyYield(),"a").start();
        new Thread(new 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()+"暂停运行");
    }
}

18、线程池

  • 频繁的创建和销毁线程耗费较多的CPU资源,并发情况下对运行性能影响较大;

  • 所以创建多个线程放入线程池,使用时直接获取,使用完放回池中;

  • 提高了响应速度,降低了资源消耗,便于管理线程,

  • corePoolSize:核心池大小

  • maximumPoolSize:最大线程数

  • keepAliveTime:线程没有任务时最多保持多长时间会终止

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPool {
    public static void main(String[] args) {
        //创建服务,创建线程池
        //newFixedThreadPool(10),参数为线程池大小
        ExecutorService service = Executors.newFixedThreadPool(10);
        //线程池启动线程
        service.execute(new MyThreads());
        service.execute(new MyThreads());
        service.execute(new MyThreads());
        service.execute(new MyThreads());
        service.shutdown();
    }
}
class MyThreads implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"开始运行");
    }
}

19、线程小结

posted on 2021-03-27 19:25  唐唐唐11  阅读(78)  评论(0)    收藏  举报