• 多线程学习


    Process 和 Tread

    创建多线程方法一

     1 package main.com;
     2 
     3 
     4 //创建进程方法一:继承 Thread 类,重写 run() 方法,调用 start() 方法开启线程
     5 
     6 //总结:注意,线程开启不一定立即执行由 CPU 调度执行
     7 public class TestThread1 extends Thread{
     8 
     9     @Override
    10     public void run(){
    11         //run 方法线程体
    12         for (int i = 0; i < 20; i++) {
    13             System.out.println("runing..." + i);
    14         }
    15 
    16     }
    17 
    18     public static void main(String[] args) {
    19         //main 线程,即主线程
    20 
    21         TestThread1 tt1 = new TestThread1();
    22         //调用start() 方法开启线程
    23         tt1.start();
    24 
    25         for (int i = 0; i < 200; i++) {
    26             System.out.println("main....." + i);
    27         }
    28 
    29     }
    30 }

     实践

     1 package com.xun;
     2 
     3 import org.apache.commons.io.FileUtils;
     4 
     5 import java.io.File;
     6 import java.io.IOException;
     7 import java.net.URL;
     8 
     9 //练习Thread,实现多线程同步下载图片
    10 public class TestThread2 extends Thread{
    11     private String url; //网络图片地址
    12     private String name;    //保存的文件名
    13 
    14     public TestThread2(String url,String name){
    15         this.url = url;
    16         this.name = name;
    17     }
    18 
    19     @Override
    20     public void run(){
    21         WebDownloder webDownloder = new WebDownloder();
    22         webDownloder.downloader(url,name);
    23         System.out.println("下载文件名为"+name);
    24     }
    25 
    26     public static void main(String[] args) {
    27         TestThread2 t1 = new TestThread2("http://www.ikuimg.com/img/pic/2203/1-220331105120-p9.jpg","1.jpg");
    28         TestThread2 t2 = new TestThread2("http://www.ikuimg.com/img/pic/2203/1-220331105121-p9.jpg","2.jpg");
    29         TestThread2 t3 = new TestThread2("http://www.ikuimg.com/img/pic/2203/1-220331105122-p9.jpg","3.jpg");
    30 
    31         t1.start();
    32         t2.start();
    33         t3.start();
    34 
    35     }
    36 }
    37 
    38 //下载器
    39 class WebDownloder{
    40     public void downloader(String link, String name){
    41         try {
    42             URL url = new URL(link);
    43             //url.openConnection().setRequestProperty("User-Agent", "Mozilla/4.76");
    44             FileUtils.copyURLToFile(url,new File(name));
    45         } catch (IOException e) {
    46             e.printStackTrace();
    47             System.out.println("IO异常,downloder方法异常");
    48         }
    49     }
    50 }
    同步下载图片

    创建多线程方法二:

     1 package com.xun;
     2 
     3 //创建线程方式2:实现 runable 接口,重写run方法,实行线程需要丢入 runable 接口实现类,调用start 方法
     4 
     5 public class TestThread3 implements Runnable{
     6     @Override
     7     public void run(){
     8         //run 方法调用线程体
     9         for (int i = 0; i < 20; i++) {
    10             System.out.println("runing...");
    11         }
    12     }
    13 
    14     public static void main(String[] args) {
    15         //main 线程,即主线程
    16 
    17         TestThread3 tt3 = new TestThread3();
    18 
    19         //创建线程对象,通过线程对象来开启我们的线程
    20 //        Thread thread = new Thread(tt3);
    21 //        tt3.start();
    22 
    23         new Thread(tt3).start();
    24         for (int i = 0; i < 200; i++) {
    25             System.out.println("main....." + i);
    26         }
    27 
    28     }
    29 }

    小结:

      继承Thread类:

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

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

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

      实现Runable接口

        实现接口Runable具有多线程能力

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

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

       实例,初识并发

     1 package com.xun;
     2 
     3 //发现问题,多个线程操作同一个资源,线程不安全,数据紊乱,并发问题
     4 public class TestThread4 implements Runnable{
     5 
     6     private int ticketNums = 10; //票数
     7 
     8     @Override
     9     public void run(){
    10         while(ticketNums>0){
    11             try {
    12                 Thread.sleep(200);
    13             } catch (InterruptedException e) {
    14                 e.printStackTrace();
    15             }
    16             System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
    17         }
    18     }
    19 
    20     public static void main(String[] args) {
    21         TestThread4 tt4 = new TestThread4();
    22 
    23         new Thread(tt4,"张三").start();
    24         new Thread(tt4,"李四").start();
    25         new Thread(tt4,"王五").start();
    26     }
    27 }

       实例:龟兔赛跑,一个对象,多个线程使用

     1 package com.xun;
     2 
     3 //发现问题,多个线程操作同一个资源,线程不安全,数据紊乱,并发问题
     4 public class TestThread4 implements Runnable{
     5 
     6     private int ticketNums = 10; //票数
     7 
     8     @Override
     9     public void run(){
    10         while(ticketNums>0){
    11             try {
    12                 Thread.sleep(200);
    13             } catch (InterruptedException e) {
    14                 e.printStackTrace();
    15             }
    16             System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
    17         }
    18     }
    19 
    20     public static void main(String[] args) {
    21         TestThread4 tt4 = new TestThread4();
    22 
    23         new Thread(tt4,"张三").start();
    24         new Thread(tt4,"李四").start();
    25         new Thread(tt4,"王五").start();
    26     }
    27 }
    View Code

       通过静态代理了解线程实现原理

     1 package com.xun;
     2 //通过静态代理了解线程底部实现原理
     3 
     4 //静态代理模式总结
     5 //真实对象和代理对象都要实现同一个接口
     6 //代理对象要代理真实对象
     7     //好处:
     8         //代理对象可以做很多真实对象做不了的事
     9         //真实对象专注做自己的事
    10 public class StaticProxy {
    11 
    12     public static void main(String[] args) {
    13         You you = new You();
    14 
    15         new Thread(()-> System.out.println("I love you!")).start();
    16 
    17         new WeddingCompany(you).HappyMarry();
    18 
    19         //you.HappyMarry();
    20 
    21        // WeddingCompany weddingCompany = new WeddingCompany(you);
    22         //weddingCompany.HappyMarry();
    23     }
    24 
    25 }
    26 
    27 interface Marry{
    28     void HappyMarry();
    29 }
    30 
    31 //真实角色,你要结婚
    32 class You implements Marry{
    33     @Override
    34     public void HappyMarry(){
    35         System.out.println("你要开心的结婚了!");
    36     }
    37 }
    38 
    39 //代理角色,帮你结婚
    40 class WeddingCompany implements Marry{
    41 
    42     private Marry target;
    43 
    44     public WeddingCompany(Marry target){
    45         this.target=target;
    46     }
    47 
    48     @Override
    49     public void HappyMarry(){
    50         before();
    51         this.target.HappyMarry();
    52         after();
    53     }
    54 
    55     private void before(){
    56         System.out.println("结婚前布置");
    57     }
    58 
    59     private void after(){
    60         System.out.println("婚后结尾款");
    61     }
    62 }
    静态代理模式

    函数式接口Functional Interface

      定义:

        任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口

        对于函数式接口,我们可以通过lambda表达式来创建接口的对象

    Lamda表达式  //JDK8新增特性

      意义:

        避免匿名内部类定义过多

        可以让你的代码看起来很简洁

        去掉了一堆没有意义的代码,只留下核心逻辑

     1 package com.xun.lambda;
     2 
     3 public class TestLambda1 {
     4 
     5     //3、静态内部类
     6     static class Like2 implements ILike{
     7 
     8         @Override
     9         public void Lambda() {
    10             System.out.println("I like lambda2");
    11         }
    12     }
    13     public static void main(String[] args) {
    14         ILike like = new Like1();
    15         like.Lambda();
    16 
    17         like = new Like2();
    18         like.Lambda();
    19 
    20         //4、局部内部类
    21         class Like3 implements ILike{
    22 
    23             @Override
    24             public void Lambda() {
    25                 System.out.println("I like lambda3");
    26             }
    27         }
    28 
    29         like = new Like3();
    30         like.Lambda();
    31 
    32         //5、匿名内部类
    33         like = new ILike() {
    34             @Override
    35             public void Lambda() {
    36                 System.out.println("I like lambda4");
    37             }
    38         };
    39         like.Lambda();
    40 
    41         //6、用lambda简化
    42         like = ()->{
    43             System.out.println("I like lambda5");
    44         };
    45         like.Lambda();
    46     }
    47 }
    48 
    49 //1、定义一个函数式接口
    50 interface ILike{
    51     void Lambda();
    52 }
    53 
    54 //2、实现类
    55 class Like1 implements ILike{
    56 
    57     @Override
    58     public void Lambda() {
    59         System.out.println("I like lambda1");
    60     }
    61 }
    lambda演化过程

    线程方法:

    方法 说明
    setPriority(int new Priority) 更改线程的优先级
    static void sleep(long millis) 在指定毫秒数内让当前正在执行的线程进入休眠
    void join() 等待该线程终止(插队)
    static void yield() 暂停当前正在执行的线程对象,并执行其他线程
    void interrupt() 中断线程(一般不用)
    boolean isAlive() 测试线程是否处于活动状态

    线程停止:

     1 package com.xun.state;
     2 
     3 public class TestStop implements Runnable{
     4 
     5     //设置标志位
     6     private boolean flag = true;
     7 
     8     @Override
     9     public void run() {
    10         int i=0;
    11         while(flag){
    12             System.out.println("runing..."+i++);
    13         }
    14 
    15     }
    16 
    17     private void stop(){
    18         this.flag = false;
    19     }
    20 
    21     public static void main(String[] args) {
    22         TestStop testStop = new TestStop();
    23         new Thread(testStop).start();
    24 
    25         for(int i=0;i<1000;i++){
    26             System.out.println("main..."+i);
    27             if(i==900){
    28                 //调用自己的stop方法
    29                 testStop.stop();
    30                 System.out.println("线程该停止了");
    31             }
    32         }
    33     }
    34 }
    标志位线程停止

    线程休眠:

      sleep 存在异常InterruptedException;

      可以模拟网络延时;

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

    线程礼让(yield)

      让当前执行线程暂停,但不阻塞,从运行态变为就绪态

      让cpu重写调度,礼让不一定成功

    线程强制执行(join)

      join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞

      可以想象成插队

     1 package com.xun.state;
     2 
     3 public class TestJoin implements Runnable{
     4 
     5     @Override
     6     public void run() {
     7         for (int i = 0; i < 1000; i++) {
     8             System.out.println("vip runing..."+i);
     9         }
    10     }
    11 
    12     public static void main(String[] args) throws InterruptedException {
    13         TestJoin testJoin = new TestJoin();
    14         Thread thread = new Thread(testJoin);
    15         thread.start();
    16 
    17         for (int i = 0; i < 200; i++) {
    18             System.out.println("main ..."+i);
    19             if(i==50){
    20                 System.out.println("vip coming ");
    21                 thread.join();
    22             }
    23         }
    24     }
    25 }
    join

    线程观察状态:

    package com.xun.state;
    
    public class TestState {
        public static void main(String[] args)  {
            Thread thread = new Thread(()->{
               for(int i=0;i<5;i++){
                   try {
                       Thread.sleep(1000);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   System.out.println("///////");
               }
            });
    
            //观察状态
            Thread.State state = thread.getState();
            System.out.println(state);
    
            //观察启动后
            thread.start();//启动
            state = thread.getState();
            System.out.println(state);
    
            while(state!=Thread.State.TERMINATED){//只要线程没终止就一直执行
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                state = thread.getState();
                System.out.println(state);
    
            }
    
        }
    }
    线程状态观察

      线程中断或结束后,一旦进入死亡状态,就不能再启动了。

    线程优先级(priority)

      1~10; get、set 方法。main方法默认优先级为5

      优先级低只表示调度概率低,并非绝对,主要看cpu调度

    守护(daemon)线程

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

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

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

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

     1 package com.xun.state;
     2 
     3 public class TestDaemon {
     4     public static void main(String[] args) {
     5         God god = new God();
     6         You you = new You();
     7         Thread thread = new Thread(god);
     8         thread.setDaemon(true); //设置为守护线程
     9         thread.start();
    10 
    11         new Thread(you).start();
    12     }
    13 }
    14 
    15 class God implements Runnable{
    16 
    17     @Override
    18     public void run() {
    19         while(true){
    20             System.out.println("god is alive");
    21         }
    22     }
    23 }
    24 
    25 class You implements Runnable{
    26 
    27     @Override
    28     public void run() {
    29         for (int i = 0; i < 36500; i++) {
    30             System.out.println("you are alive ");
    31         }
    32         System.out.println("GoodBye,World!");
    33     }
    34 }
    守护线程

    线程同步

      同一个资源,多个人想使用,最天然的解决办法就是排队,一个一个来

      处理多线程问题是,多个线程访问同一个对象(并发),并且某些线程想修改这个对象。这时候我们就需要线程同步。线程同步其实就是一个等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。

      形成条件:队列 + 锁

      由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题,为了保证数据在方法中被访问时的正确性,在访问时加入 锁机制 synchronized,当一个线程获得对象的排他锁,独占资源,其他线程必须等待使用后释放锁即可。存在以下问题:

        一个线程持有锁会导致其他所有需要此锁的线程挂起;

        在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;

        如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。

      线程不安全的3个例子

     1 package com.xun.syn;
     2 
     3 //不安全的买票
     4 //有重票和负数
     5 public class UnSafeBuyTicket {
     6     public static void main(String[] args) {
     7         BuyTicket buyTicket = new BuyTicket();
     8 
     9         new Thread(buyTicket,"ant").start();
    10         new Thread(buyTicket,"bord").start();
    11         new Thread(buyTicket,"cat").start();
    12 
    13     }
    14 
    15 }
    16 
    17 class BuyTicket implements Runnable{
    18 
    19     //
    20     private int ticketNum = 10;
    21     //外部停止方式
    22     private boolean flag = true;
    23 
    24     @Override
    25     public void run() {
    26         //买票
    27         while(flag){
    28             buy();
    29         }
    30 
    31     }
    32     private void buy(){
    33         if(ticketNum<=0){
    34             flag=false;
    35             return ;
    36         }
    37         //模拟延时
    38         try {
    39             Thread.sleep(100);
    40         } catch (InterruptedException e) {
    41             e.printStackTrace();
    42         }
    43         System.out.println(Thread.currentThread().getName()+"拿到票"+ticketNum--);
    44     }
    45 }
    UnSafeBuyTicket
     1 package com.xun.syn;
     2 
     3 public class UnSafeBank {
     4     public static void main(String[] args) {
     5         Account account = new Account(100,"Marry money");
     6 
     7         Drawing you = new Drawing(50,"you",account);
     8         Drawing gril = new Drawing(100,"gril",account);
     9 
    10         you.start();
    11         gril.start();
    12     }
    13 }
    14 
    15 //账户
    16 class Account{
    17     String name;
    18     int money;
    19 
    20     public Account(int money,String name){
    21         this.money = money;
    22         this.name = name;
    23     }
    24 }
    25 
    26 //银行
    27 class Drawing extends Thread{
    28     Account account;
    29     int drawingMoney;
    30     int nowMoney;
    31 
    32     public Drawing(int drawingMoney,String name,Account account){
    33         super(name);
    34         this.account=account;
    35         this.drawingMoney=drawingMoney;
    36     }
    37 
    38     //取钱
    39     @Override
    40     public void run(){
    41         //判断有没有钱
    42         if(account.money-drawingMoney<0){
    43             System.out.println(Thread.currentThread().getName()+"钱不够,取不了");
    44             return;
    45         }
    46         try {
    47             Thread.sleep(100);
    48         } catch (InterruptedException e) {
    49             e.printStackTrace();
    50         }
    51         account.money-=drawingMoney;
    52         nowMoney+=drawingMoney;
    53 
    54         System.out.println(account.name+"余额为"+account.money);
    55         //Thread.currentThread().getName()等价于this.getName(),因为继承自Thread
    56         System.out.println(this.getName()+"手里的钱"+nowMoney);
    57     }
    58 }
    UnSafeBank
    package com.xun.syn;
    
    import java.util.ArrayList;
    import java.util.List;
    
    //线程不安全的集合,加到同一位置了
    public class UnSafeList {
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            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());
        }
    }
    UnSafeList
  • 相关阅读:
    JQuery 快速入门一篇通
    Winform 显示Gif图片
    MD5编码工具类 MD5Code.java
    Asp.Net 上传图片并生成高清晰缩略图
    winform时钟c#代码
    PHP面试题汇总
    PHP条件语句语法与示例
    Android声音播放实例代码
    html和js基础功能代码备份
    纯C#实现屏幕指定区域截屏
  • 原文地址:https://www.cnblogs.com/xunzf0402/p/16150841.html
Copyright © 2020-2023  润新知