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会让等待该锁的所有线程都会被唤醒。

    线程池

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

    好处

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

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

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

  • 相关阅读:
    uva 10370
    uva 10107
    uva 10038
    uva 488
    伪代码格式
    公众号的秘密,知道一个biz就够了
    ToolTip 概述
    swt
    Java GUI图形界面开发工具
    Java多线程-两个小球
  • 原文地址:https://www.cnblogs.com/huacha/p/14052153.html
Copyright © 2011-2022 走看看