zoukankan      html  css  js  c++  java
  • 多线程学习

    核心概念

    • 线程就是独立的执行路径

    • Main()称之为主线程,为系统的入口,用于执行整个程序

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

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

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

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


    Demo1 简单理解线程

    public class Demo1 extends Thread {
        @Override
        public void run() {
            //super.run();
            for (int i = 0; i < 20; i++) {
                System.out.println(" 子线程在跑 "+ i);
            }
    
        }
    
        public static void main(String[] args) {
            Demo1 demo1 =new Demo1();
            //demo1.run();//普通的执行run,正常顺序
            demo1.start();//创建子线程,执行run,主线程和子线程的执行,由CPU进行调度,无法人为干预
            //
            for (int i = 0; i < 100; i++) {
                System.out.println("主线程在跑"+i);
            }
        }
    }
    /*
    主线程在跑0
     子线程在跑 0
     子线程在跑 1
     子线程在跑 2
     子线程在跑 3
     子线程在跑 4
    主线程在跑1
    主线程在跑2
    主线程在跑3
    主线程在跑4
    主线程在跑5
    主线程在跑6
    主线程在跑7
    主线程在跑8
    主线程在跑9
    主线程在跑10
    主线程在跑11
    主线程在跑12
    主线程在跑13
    主线程在跑14
    主线程在跑15
    主线程在跑16
    主线程在跑17
     子线程在跑 5
     子线程在跑 6
    主线程在跑18
    主线程在跑19
     子线程在跑 7
     子线程在跑 8
     子线程在跑 9
     子线程在跑 10
     子线程在跑 11
     子线程在跑 12
     子线程在跑 13
     子线程在跑 14
     子线程在跑 15
     子线程在跑 16
     子线程在跑 17
     子线程在跑 18
     子线程在跑 19
    主线程在跑20
    主线程在跑21
    */
    

    Demo2 下载网图

    import org.apache.commons.io.FileUtils;
    
    import java.io.File;
    import java.io.IOException;
    import java.net.URL;
    
    public class Demo2 {
        int k=0;
        public static void main(String[] args) {
            WebDownloader wd =new WebDownloader("https://i0.hdslb.com/bfs/album/92033a2dfd1630bf3d013965e1769258ee88eb35.png","1.png");
            WebDownloader wd2 =new WebDownloader("https://i0.hdslb.com/bfs/album/cebd6f76c2317c472e6a0a5610fea0882931cbc3.jpg","2.jpg");
            wd.start();
            wd2.start();
            /*
            下载了:1.png
            下载了:2.jpg
             */
        }
    }
    class WebDownloader extends Thread{
        private String Url;
        private String ServerFileName;
        public WebDownloader(String url,String serverFileName)
        {
            this.Url = url;
            this.ServerFileName=serverFileName;
        }
    
        @Override
        public void run() {
            //super.run();
            try {
                //copyURLToFile 方法抛出了异常,上层函数一定要处理这个异常
                FileUtils.copyURLToFile(new URL(this.Url), new File(this.ServerFileName));
                System.out.println("下载了:"+this.ServerFileName);
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("下载图片报错");
            }
        }
    }
    

    Demo3 使用Runable接口实现多线程

    public class Demo3 implements Runnable {
    
        @Override
        public void run() {
            //super.run();
            for (int i = 0; i < 20; i++) {
                System.out.println(" 子线程在跑 "+ i);
            }
        }
    
        public static void main(String[] args) {
            Demo3 demo3 =new Demo3();
            new Thread(demo3).start();//调用方式稍作改变
            //
            for (int i = 0; i < 100; i++) {
                System.out.println("主线程在跑"+i);
            }
        }
    }
    

    Demo4 使用Callable的接口实现多线程

    public class Demo5  {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            WebDownloader2 wd =new WebDownloader2("https://i0.hdslb.com/bfs/album/92033a2dfd1630bf3d013965e1769258ee88eb35.png","1.png");
            WebDownloader2 wd2 =new WebDownloader2("https://i0.hdslb.com/bfs/album/cebd6f76c2317c472e6a0a5610fea0882931cbc3.jpg","2.jpg");
            ExecutorService service = Executors.newFixedThreadPool(2);
            Future<Boolean> res1 = service.submit(wd);
            Future<Boolean> res2 = service.submit(wd2);
            Boolean r1 = res1.get();
            Boolean r2 = res2.get();
            System.out.println(r1);
            System.out.println(r2);
        }
    }
    class WebDownloader2 implements Callable<Boolean> {
        private String Url;
        private String ServerFileName;
        public WebDownloader2(String url,String serverFileName)
        {
            this.Url = url;
            this.ServerFileName=serverFileName;
        }
        @Override
        public Boolean call() {
            //super.run();
            try {
                //copyURLToFile 方法抛出了异常,上层函数一定要处理这个异常
                FileUtils.copyURLToFile(new URL(this.Url), new File(this.ServerFileName));
                System.out.println("下载了:"+this.ServerFileName);
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("下载图片报错");
            }
            return true;
        }
    
    }
    

    静态代理模式

    Thread 实现对象执行方法的模式

    //总结:
    //真是执行对象和代理对象都要集成同一个接口
    //代理对象要代理真实角色
    //好处
    //1.代理对象可以做很多真实对象做不了的事,或者说不好做的事
    //2.真实对象专注自己的业务
    public class Demo6 {
        public static void main(String[] args) {
            Person person = new Person();
            //静态代理类传入 被代理对象
            MarryCompany marryCompany = new MarryCompany(person);
            marryCompany.HappyMarry();//静态代理类,帮助被代理对象执行
            //Tread 类实现新线程执行对象方法就是静态代理模式
            new Thread(()-> System.out.println("Tread 类实现新线程执行对象方法就是静态代理模式")).start();
        }
    }
    
    /**
     * Marry接口
     */
    interface Marry
    {
        void HappyMarry();
    }
    
    /**
     * Person类,静态代理实际执行的对象类
     */
    class Person implements Marry
    {
        @Override
        public void HappyMarry() {
            System.out.println("结婚啦");
        }
    }
    /**
    * MarryCompany Marry代理类
     */
    class MarryCompany implements Marry
    {
        private Marry _marryPerson;
        public MarryCompany(Marry marryPerson)
        {
            this._marryPerson =marryPerson;
        }
        @Override
        public void HappyMarry() {
            System.out.println("收定金,布置会场");
            this._marryPerson.HappyMarry();
            System.out.println("收尾款");
        }
    }
    

    线程状态

    五种状态

    创建状态 就绪状态 阻塞状态 运行状态 死亡状态

    image-20201119154418198

    Sleep

    Sleep阻塞线程,模拟网络延时可以放大问题的放生性。

    public class Demo7 {
        public static void main(String[] args) throws InterruptedException {
            Date currentTime = new Date(System.currentTimeMillis());
            for (int i = 0; i < 10; i++) {
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(currentTime));//java操作时间和c#写起来感觉还是有很大区别的
                Thread.sleep(1000);
                currentTime = new Date(System.currentTimeMillis());
            }
        }
    }
    

    线程礼让 yeild

    让CPU重新调度,礼让不一定成功,看CPU调度

    public class Demo8 {
        public static void main(String[] args) {
            TestYeild testYeild1 = new TestYeild();
            TestYeild testYeild2 = new TestYeild();
            new Thread(testYeild1,"a").start();
            new Thread(testYeild2,"b").start();
        }
    }
    class TestYeild implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"开始运行");
            Thread.yield();
            System.out.println(Thread.currentThread().getName()+"结束运行");
        }
    }
    

    Join

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

    public class Demo9 {
        public static void main(String[] args) throws InterruptedException {
            TestJoin testJoin = new TestJoin();
            Thread thread = new Thread(testJoin);
            for (int i = 0; i < 100; i++) {
                if(i==0)
                {
                    thread.start();
                }
                if(i==10)
                {
                    System.out.println("开始插队=================");
                    thread.join();
                }
                System.out.println("主线程"+i);
            }
        }
    }
    class TestJoin implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 50; i++) {
                System.out.println("插队线程"+i);
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("插队还不快点,还睡觉==============");
        }
    }
    /*
    主线程0
    主线程1
    主线程2
    主线程3
    主线程4
    主线程5
    主线程6
    主线程7
    主线程8
    插队线程0
    主线程9
    开始插队=================
    插队线程1
    插队线程2
    插队线程3
    插队线程4
    插队线程5
    插队线程6
    ....
    插队线程43
    插队线程44
    插队线程45
    插队线程46
    插队线程47
    插队线程48
    插队线程49
    插队还不快点,还睡觉==============
    主线程10
    主线程11
    主线程12
    ....
    主线程88
    主线程89
    主线程90
    主线程91
    主线程92
    主线程93
    主线程94
    主线程95
    主线程96
    主线程97
    主线程98
    主线程99
    
    Process finished with exit code 0
    
    */
    

    线程优先级

    线程优先级高不一定先执行,但权重高!

    min=1 max=10 normal=5

    先设置优先级再启动

    守护线程

    线程分为用户线程和守护线程。虚拟机必须确保用户线程执行完毕。虚拟机不用等待守护线程执行完毕。如后台记录操作日志,监控内存,垃圾回收等待。

    public class Demo10 {
        public static void main(String[] args) {
            DeamonThread deamon = new DeamonThread();
            UserThread user = new UserThread();
            Thread deamonThread = new Thread(deamon);
            deamonThread.setDaemon(true);//守护线程默认为false
            deamonThread.start();
            Thread userThread = new Thread(user);
            userThread.start();
        }
    }
    class UserThread implements Runnable
    {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println("用户线程执行第"+i);
            }
        }
    }
    class DeamonThread implements Runnable
    {
        @Override
        public void run() {
            //当主线程和其他用户线程执行完毕之后,守护线程也随之关闭
            while(true){
                System.out.println("守护线程执行中");
            }
        }
    }
    

    同步方法和同步快

    public class Demo4 implements Runnable {
        int ticket = 10;
        @Override
        public synchronized void run() {//锁住run方法
            while (ticket>0)
            {
                System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket--+"张票");
            }
        }
    
        public static void main(String[] args) {
            Demo4 demo4 = new Demo4();
            new Thread(demo4,"小明").start();
            new Thread(demo4,"小红").start();
            new Thread(demo4,"小蓝").start();
        }
    }
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class Demo11 {
        public static void main(String[] args) throws InterruptedException {
            List<String> list = new ArrayList<String>();//这里实例化跟C#不太一样
            for (int i = 0; i < 20000; i++) {
                new Thread(()->{
                    synchronized (list)
                    {
                        list.add(Thread.currentThread().getName());
                    }
                }).start();
            }
            Thread.sleep(6000);
            System.out.println(list.size());//不加 synchronized的情况下 19997!=20000,add当前对象的相同位置,覆盖了
        }
    }
    
    public class Demo12 {
        public static void main(String[] args) {
            Account account = new Account("共同账户",100);
            Withdraw withdraw = new Withdraw(account,100);
            new Thread(withdraw).start();
            new Thread(withdraw).start();
            new Thread(withdraw).start();
            new Thread(withdraw).start();
        }
    }
    
    class Account{
        String name;
        int money;
        public Account(String name, int money) {
            this.name = name;
            this.money = money;
        }
    }
    
    class Withdraw extends Thread
    {
        Account account;
        int withdraw;
        int nowMoney;
        public Withdraw(Account account, int withdraw) {
            this.account = account;
            this.withdraw = withdraw;
        }
    
        @Override
        public void run()
        {
            try {
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (account)
            {
                if(account.money>0 && account.money>=withdraw)
                {
                    int rs = this.account.money-this.withdraw;
                    this.account.money -=this.withdraw;
                    this.nowMoney += this.withdraw;
                    System.out.println("余额"+rs+";现在手中的钱有:"+nowMoney);
                }
                else
                {
                    System.out.println("余额不足");
                }
            }
        }
    }
    
    import java.util.concurrent.CopyOnWriteArrayList;
    
    public class Demo13 {
        public static void main(String[] args) throws InterruptedException {
            CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();//这里实例化跟C#不太一样
            for (int i = 0; i < 20000; i++) {
                new Thread(()->{
                    list.add(Thread.currentThread().getName());
                }).start();
            }
            Thread.sleep(6000);
            System.out.println(list.size());//CopyOnWriteArrayList 线程安全类
        }
    }
    
    

    生产者消费者问题

    解决方案一:

    并发写作模型“生产者/消费者模式”-->管程法

    生产者:负责生产数据的模块

    消费者:负责处理数据的模块

    缓冲区:消费者不能直接使用生产者的数据,消费者从缓冲区拿出数据

    解决方案二:

    并发协作模型“生产者/消费者模式”-->信号灯法

    通过标志位,判断是否可以通行

    public class Demo14 {
        public static void main(String[] args) {
            SynContainer synContainer = new SynContainer();
            Cook cook =new Cook(synContainer);
            Custom custom = new Custom(synContainer);
            new Thread(cook,"cook").start();
            new Thread(custom,"Custom").start();
    
        }
    }
    //产品 鸡
    class Chicken
    {
        public int id;
    
        public Chicken(int id) {
            this.id = id;
        }
    }
    //生产者
    class Cook implements Runnable
    {
        SynContainer synContainer;
        public Cook(SynContainer synContainer)
        {
            this.synContainer = synContainer;
        }
    
        @Override
        public void run() {
            // 生产者生产 产品
            for (int i = 0; i < 100; i++) {
                Chicken chicken = new Chicken(i);
                this.synContainer.push(chicken);
                System.out.println("生产者生产了第"+i+"只鸡");
            }
        }
    
    }
    //消费者 顾客
    class Custom implements Runnable
    {
        SynContainer synContainer;
        public Custom(SynContainer synContainer)
        {
            this.synContainer = synContainer;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                Chicken chicken = this.synContainer.pop();
                System.out.println("消费者消费了第"+chicken.id+"只鸡");
            }
        }
    }
    
    class SynContainer
    {
        Chicken[] chickens = new Chicken[10];
        int count = 0;
    
        public synchronized void push(Chicken chicken)
        {
            //容器满了,等待生产者消费
            if(chickens.length == count)
            {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            this.chickens[count]=chicken;
            count++;
            //通知消费者,可以消费了
            this.notifyAll();
        }
    
        public synchronized Chicken pop() {
            if (count == 0) {
                //等待生产者生产
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            Chicken chicken = this.chickens[count-1];
            //this.chickens[count] = null;
            count--;
            this.notifyAll();
            return chicken;
        }
    }
    

    notify和notifyAll的差别:notify只唤醒一个线程,且不保证能那个线程会被唤醒,这取决于线程调度器。notifyAll会让等待该锁的所有线程都会被唤醒。

    线程池

    思路:提前创建好多个线程池,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。

    好处

    提高相应速度(减少了创建新线程的时间)

    降低消耗资源(重复利用线程池中线程,不需要每次创建)

    便于管理(核心池的大小(放多少线程)、最大线程数(同时跑多少线程)、线程没有任务时最多保持多长时间)

  • 相关阅读:
    中国历史朝代公元对照简表
    [Solved] DashBoard – Excel Service: The data sources may be unreachable, may not be responding, or may have denied you access.
    Delete/Remove Project from TFS 2010
    Sharepoint site showing system account instead of my username on the top right corner.
    你的成功在于你每天养成的习惯
    Internet Information Services is running in 32bit emulation mode. Correct the issue listed above and rerun setup.
    Prepare to back up and restore a farm (Office SharePoint Server 2007)
    Word中字号与磅值的对应关系
    How to: Change the Frequency for Refreshing the Data Warehouse for Team System
    UI Automation in WPF/Silverlight
  • 原文地址:https://www.cnblogs.com/huacha/p/14052153.html
Copyright © 2011-2022 走看看