zoukankan      html  css  js  c++  java
  • java多线程相关代码

    1.创建线程的三种方式

      使用Thread

    package com.wpbxx.test;
    
    //1.自定义一个类,继承java.lang包下的Thread类
    class MyThread extends Thread{
        //2.重写run方法
        @Override
        public void run() {
            //3.将要在线程中执行的代码编写在run方法中
            for(int i = 0; i < 1000; i++) {
                System.out.println("wpb");
            }
        }
    }
    public class helloworld {
    
        public static void main(String[] args) {
            //4.创建上面自定义类的对象
            MyThread mt  = new MyThread();
            //5.调用start方法启动线程
            mt.start();
            for(int i = 0; i< 1000; i++) {
                System.out.println("xx");
            }
        }
    
    }

    使用Runnable

    package com.wpbxx.test;
    
    //1.自定义一个类实现java.lang包下的Runnable接口
    class MyRunnable implements Runnable{
        //2.重写run方法
            @Override
        public void run() {
                //3.将要在线程中执行的代码编写在run方法中
            for(int i = 0; i < 1000; i++) {
                System.out.println("wpb");
            }
        }
    }
    public class helloworld {
    
        public static void main(String[] args) {
            //4.创建上面自定义类的对象
            MyRunnable mr = new MyRunnable();
            //5.创建Thread对象并将上面自定义类的对象作为参数传递给Thread的构造方法
            Thread t = new Thread(mr);
            //6.调用start方法启动线程
            t.start();
            for(int i = 0; i < 1000; i++) {
                System.out.println("xx");
            }
        }
    
    }

    使用Callable接口创建的线程会获得一个返回值并且可以声明异常。

    优点: 可以获取返回值 可以抛出异常

    线程池

    线程池是初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时直接去这个线程集合中获取,而不是创建一个线程。任务执行结束后,线程回到池子中等待下一次的分配。

    线程池的作用
    解决创建单个线程耗费时间和资源的问题。

    package com.wpbxx.test;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    //1.自定义一个类实现java.util.concurrent包下的Callable接口
    class MyCallable implements Callable<Integer>{
        private int count;
        public MyCallable(int count) {
            this.count = count;
        }
        //2.重写call方法
            @Override
        public Integer call() throws Exception{
                //3.将要在线程中执行的代码编写在call方法中
            for(int i = 0; i < 100; i++) {
                count++;
            }
            return count;
        }
    }
    public class helloworld {
    
        public static void main(String[] args) {
            //4.创建ExecutorService线程池 里面为线程的数量
            ExecutorService es = Executors.newFixedThreadPool(2);
            ////创建一个线程池,里面的线程会根据任务数量进行添加
            //ExecutorService es = Executors.newCachedThreadPool();
        
            //5.将自定义类的对象放入线程池里面
            Future<Integer> f1= es.submit(new MyCallable(5));
            Future<Integer> f2 = es.submit(new MyCallable(3));
            try {
                //6.获取线程的返回结果
                System.out.println(f1.get());
                System.out.println(f2.get());
                
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ExecutionException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //7.关闭线程池,不再接收新的线程,未执行完的线程不会被关闭
            es.shutdown();
        }
    
    }

    继承Thread
      优点:可以直接使用Thread类中的方法,代码简单
      缺点:继承Thread类之后就不能继承其他的类
    实现Runnable接口
      优点:即时自定义类已经有父类了也不受影响,因为可以实现多个接口
      缺点: 在run方法内部需要获取到当前线程的Thread对象后才能使用Thread中的方法
    实现Callable接口
      优点:可以获取返回值,可以抛出异常
      缺点:代码编写较为复杂

    package com.wpbxx.test;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    //简易写法  使用匿名内部类创建多线程
    
    
    public class helloworld {
    
        public static void main(String[] args) throws InterruptedException, ExecutionException {
            new Thread() {
                public void run() {
                    for(int i = 0; i < 1000; i++) {
                        System.out.println("wpb");
                    }
                }
            }.start();
            
            new Thread(new Runnable() {
                public void run() {
                    for(int i = 0; i< 1000; i++) {
                        System.out.println("xx");
                    }
                }
            }).start();
            
            ExecutorService exec = Executors.newCachedThreadPool();
            Future<Integer> result = exec.submit(new Callable<Integer>() {
                
            @Override
            public Integer call() throws Exception{
                return 1024;
            }
            });
            
            System.out.println(result.get());
        }
    
    }

    Thread设置线程的名字

    方法一

    new Thread("马化腾") {                            //通过构造方法给name赋值
                public void run() {
                    System.out.println("我是" + this.getName() + ",来腾讯工作吧 ");
                }
            }.start();

    方法二

    new Thread() {
                public void run() {
                    this.setName("马化腾");      //调用setName
                    System.out.println("我是" + this.getName() + ",来腾讯啊");
                }
            }.start();

    使用Thread.currentThread() 获得正在运行的线程

    可以这样改变Runnable中线程名字

    package com.wpbxx.test;
    
    public class helloworld {
    
        public static void main(String[] args) {
          new Thread(new Runnable() {
              public void run() {
              System.out.println(Thread.currentThread().getName());
              Thread.currentThread().setName("wpb");
              System.out.println(Thread.currentThread().getName());
              }
          }).start();
        }
    
    }

    线程睡眠

    Thread中的sleep方法可以使当前线程睡眠,线程睡眠后,里面的任务不会执行,待睡眠时间过后会自动苏醒,从而继续执行任务。

    Thread.sleep(1000);   //让当前线程睡眠1秒

    线程的优先级

    setPriority()方法接收一个int类型的参数,通过这个参数可以指定线程的优先级,取值范围是整数1~10,优先级随着数字的增大而增强。  但并不是一定执行优先级高的执行完之后  才执行别的

    package com.wpbxx.test;
    
    public class helloworld {
    
        public static void main(String[] args) {
            Thread t1 = new Thread() {
                public void run() {
                    for(int i = 0; i<100; i++) {
                        System.out.println("wpb");
                    }
                }
            };
            
            Thread t2 = new Thread() {
                public void run() {
                    for(int i = 0; i < 100; i++) {
                        System.out.println("1024");
                    }
                }
            };
            
            t1.setPriority(10);
            t2.setPriority(0);
            t1.start();
            t2.start();
            
            
        }
    
    }

    唤醒睡眠中的线程

      t1.interrupt();

    用interrupt方法会抛出一个InterruptedException的异常。

    同步方法

    package com.wpbxx.test;
    
    public class helloworld {
    
        public static void main(String[] args) {
        
            Task tk = new Task();
            
            Thread t1 = new Thread() {
                public void run() {
                    tk.changeNum(true);
                }
            };
            Thread t2 = new Thread() {
                public void run() {
                    tk.changeNum(false);
                }
            };
            t1.start();
            t2.start();
        }
    
    }
    class Task{
        
        private int num = 0;
        public void changeNum(boolean flag) {
            if(flag) {
                num = 99;
                System.out.println(Thread.currentThread().getName() + "-------" + "begin");
                System.out.println(Thread.currentThread().getName() + "-------" + num);
                System.out.println(Thread.currentThread().getName() + "-------" + "end");
            }else {
                num = 22;
                System.out.println(Thread.currentThread().getName() + "-------" + "begin");
                System.out.println(Thread.currentThread().getName() + "-------" + num);
                System.out.println(Thread.currentThread().getName() + "-------" + "end");
            }
        }
    }

    正常情况下应该打印出一个88一个66,可是上面却两个线程打印出的两个66,这样就出现了线程安全的问题,出现这个问题的原因是成员变量存储在堆内存中,两个线程共享堆内存,即两个线程可以对同一个num进行修改。
    程序执行分析:
    cpu执行t1线程,将num修改为88,之后cpu开始执行t2线程,将num修改为66,打印出66,cpu开始执行t1线程,打印num的值,此时num的值是66。

    在方法上加入synchronized关键字,这样在执行多个线程时看哪个线程先执行这个方法,假设有t1,t2,t3三个线程中都调用了changeNum方法,t1线程先执行了这个方法,那么t1会先在Task对象上面加锁,加锁后,别的线程就无法执行当前Task对象上的changeNum方法,直到t1执行结束changeNum方法之后,t2,t3中的一个线程才可以执行这个方法,这就保证了在某个时间段内只有一个线程执行changeNum方法,解决了线程安全问题。
    注意:synchronized锁住的是当前对象,如果t1线程和t2线程里面是不同的对象,则不需要同步,因为不会发生线程安全问题

    public synchronized void changeNum(boolean flag)  加这一句就ok了


    也可以对需要互斥访问的代码块加上synchronized
        public void changeNum(boolean flag){
    
            try {
                Thread.sleep(3000);
                System.out.println("执行一个耗时较长的任务");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            //这个方法中,需要同步的代码块是这部分,而上面耗时操作的代码,不涉及到线程安全问题,所以不需要同步
            synchronized(obj){
                if(flag){
                    num = 88;
    
                    System.out.println(Thread.currentThread().getName() + "========== begin");
                    System.out.println(Thread.currentThread().getName() + "==========" + num);
                    System.out.println(Thread.currentThread().getName() + "========== end");
                }else{
                    num = 66;
    
                    System.out.println(Thread.currentThread().getName() + "========== begin");
                    System.out.println(Thread.currentThread().getName() + "==========" + num);
                    System.out.println(Thread.currentThread().getName() + "========== end");
                }
            }
    
        }

    死锁

    发生死锁原因就是两个或多个线程都在等待对方释放锁导致,下面通过代码来演示一下死锁情况。

    package com.wpbxx.test;
    
    public class helloworld {
    
        private static Object obj1 = new Object();
        private static Object obj2 = new Object();
        public static void main(String[] args) {
            new Thread() {
                public void run() {
                    synchronized(obj1) {
                        System.out.println(this.getName());
                        
                        synchronized(obj2) {
                            System.out.println(this.getName());
                        }
                    }
                }
            }.start();
             new Thread() {
                public void run() {
                    synchronized(obj2) {
                        System.out.println(this.getName());
                        
                        synchronized(obj1) {
                            System.out.println(this.getName());
                        }
                    }
                }
            }.start();
        }
    }

    volatile关键字

    package com.wpbxx.test;
    
    public class helloworld {
    
        public static void main(String[] args) throws InterruptedException {
            
            Task task = new Task();
            
            Thread t1 = new Thread(task);
            
            t1.start();
            
            Thread.sleep(100);
            task.setFlag(false);
        }
    }
    class Task implements Runnable{
        
        private boolean flag = true;
        
        public boolean isFlag() {
            return flag;
        }
        
        public void setFlag(boolean flag) {
            this.flag = flag;
        }
        
        public void run() {
            while(flag) {
                System.out.println("while循环");
            }
            System.out.println("循环结束");
        }
    }

    上面程序中在64位的机器上以server模式运行时,有可能会出现死循环的现象。

    JVM的运行可以分为下面两种模式:

    • client:启动快,运行后性能不如server模式,一般运行时默认是client模式
    • server:启动慢,运行后性能比client模式好。

    在eclipse中可以通过配置来使用server模式,右键—>run as—>run configurations。写上-server。然后点击run即可

    上面程序出现问题的原因这样的,虽然在主线程中将flag的设置为false,但是jvm为了提升效率,t1线程一直在私有内存中获取flag的值,而私有内存中的flag值并没有被改变,所以导致死循环的发生。

    使用volatile修饰flag解决上面问题:

    volatile private boolean flag = true;
    

    将flag声明为volatile后,t1线程会从公共的内存中访问flag的值,这样在主线程将flag设置为false后,t1线程中的循环就会结束了。

    注意:volatile只能修饰变量,不能修饰方法

    原子性和非原子性

    原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

    非原子性:不符合原子性的就是非原子性

      int x = 1024; //原子性
    
        int y = x; //cpu先去内存中读取x的值,读取后在为y进行赋值,在读取后给y赋值前的这段时间可能会切换到其他线程上面。
    
        x++; //包含了三个操作,先读取x的值,然后进行加1操作,最后写入新的值,在这三个操作的间隙可能会切换到其他线程上面。
    
        x = x + 1; //同上

    volatile是非原子性的。
    synchronized是原子性的。

     TimerTask

    TimerTask是一个实现了Runnable接口的抽象类,需要编写一个类继承TimerTask类,将要在定时任务执行的代码编写在run方法中。

    要想执行定时任务,需要创建Timer的对象并调用里面的schedule方法,在Timer类中有多个重载的schedule方法,这里咱们使用这个:

    schedule(TimerTask task, Date firstTime, long period);

    package com.wpbxx.test;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Timer;
    import java.util.TimerTask;
    
    public class helloworld {
    
        public static void main(String[] args) throws InterruptedException, ParseException {
            
            Timer t = new Timer();
            t.schedule(new MyTimerTask(), new SimpleDateFormat("yyyy-MM-dd hh:mm:ss SSS").parse("2017-07-03 18:09:00 000"),
                    5000);
        }
    }
    class MyTimerTask extends TimerTask{
        @Override
        public void run() {
            System.out.println("wpbxx");
        }
    }

    线程之间的通信

    多线程环境下CPU会随机的在线程之间进行切换,如果想让两个线程有规律的去执行,那就需要两个线程之间进行通信,在Object类中的两个方法wait和notify可以实现通信。

    wait方法可以使当前线程进入到等待状态,在没有被唤醒的情况下,线程会一直保持等待状态。
    notify方法可以随机唤醒单个在等待状态下的线程

    利用wait  和notify  进行交替打印

    package com.wpbxx.test;
    
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Timer;
    import java.util.TimerTask;
    
    public class helloworld {
    
        public static void main(String[] args) throws InterruptedException, ParseException {
            Print p = new Print();
            Thread t1 = new Thread() {
                public void run() {
                    while(true) {
                        p.print1();
                    }
                }
            };
            Thread t2 = new Thread() {
                public void run() {
                    while(true) {
                        p.print2();
                    }
                }
            };
            t1.start();
            t2.start();
        }
    }
    class Print{
        private int flag = 1;
        
        public void print1() {
            synchronized(this) {
                if(flag != 1) {
                    try {
                        this.wait();
                    }catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                
                System.out.println("wpb");
                flag = 2;
                this.notify();
            }
        }
        public void print2() {
            synchronized(this) {
                if(flag != 2) {
                    try {
                        this.wait();
                    }catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                
                System.out.println("xx");
                flag = 1;
                this.notify();
            }
        }
    }

    但这样如果是三个线程以上的  就不行,  可能出现死锁了

    因为是随机唤醒一个等待的线程,  假设一线程 进行玩后 随即唤醒一个线程,并把flag = 2,  但这时唤醒了线程3  就会一直等待

    notifyAll()  为唤醒所有的线程

    package com.wpbxx.test;
    
    /**
     * 三个(三个以上)线程之间的通信
     *
     */
    public class helloworld {
    
        public static void main(String[] args) {
    
            Print1 p = new Print1();
    
            Thread t1 = new Thread(){
                public void run(){
                    while(true){
                        p.print1();
                    }
    
                }
            };
    
            Thread t2 = new Thread(){
                public void run(){
                    while(true){
                        p.print2();
                    }
                }
            };
    
            Thread t3 = new Thread(){
                public void run(){
                    while(true){
                        p.print3();
                    }
                }
            };
    
            t1.start();
            t2.start();
            t3.start();
        }
    
    }
    
    class Print1{
    
        private int flag = 1;
    
        public void print1(){
            synchronized(this){
                while(flag != 1){
                    try {
                        //让当前线程进入等待状态
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
                System.out.println("monkey");
                flag = 2;
                //唤醒所有等待的线程
                this.notifyAll();
            }
    
        }
    
        public void print2(){
            synchronized(this){
                while(flag != 2){
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
                System.out.println("1024");
                flag = 3;
                this.notifyAll();
            }
    
        }
    
        public void print3(){
            synchronized(this){
                while(flag != 3){
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
                System.out.println("888");
                flag = 1;
                this.notifyAll();
            }
    
        }
    }
    View Code

    这样就可以实现三个线程的交替打印, 但会有问题  就是唤醒所有的线程  开销太大。

    上面notify() 或者 notifyAll()  并不能唤醒指定的线程,所以多出了  互斥锁

    新增了  ReenTrantLock类 和    Condition接口  来替换   synchronized关键字   和   wait、notify  方法。

    ReenTrantLock类     和Condition接口    都在java.util.concurrent.locks包下。
    可以使用       ReentrantLock类中    的  lock方法   和   unlock方法     进行上锁和解锁,用来替代synchronized关键字。
    Condition接口中的await方法和signal方法用来让线程等待和唤醒指定线程。用来替代wait方法和notify方法。

    如 还是循环打印东西

    package com.wpbxx.test;
    
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class helloworld {
    
        public static void main(String[] args) throws InterruptedException {
            
            Print p = new Print();
            Thread t1 = new Thread() {
                public void run() {
                    while(true) {
                        p.print1();
                    }
                }
            };
            Thread t2 = new Thread() {
                public void run() {
                    while(true) {
                        p.print2();
                    }
                }
            };
            Thread t3 = new Thread() {
                public void run() {
                    while(true) {
                        p.print3();
                    }
                }
            };
            t1.start();
            t2.start();
            t3.start();
        }
    }
    class Print{
        private ReentrantLock r = new ReentrantLock();
        
        private Condition c1 = r.newCondition();
        private Condition c2 = r.newCondition();
        private Condition c3 = r.newCondition();
        
        private int flag = 1;
        public void print1() {
            r.lock();
            
            while(flag != 1) {
                try {
                    c1.await();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            System.out.println("wpb1");
            flag = 2;
            c2.signal();
            r.unlock();
        }
        
        public void print2() {
            r.lock();
            
            while(flag != 2) {
                try {
                    c2.await();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            System.out.println("wpb2");
            flag = 3;
            c3.signal();
            r.unlock();
        }
        public void print3() {
            r.lock();
            
            while(flag != 3) {
                try {
                    c3.await();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            System.out.println("wpb3");
            flag = 1;
            c1.signal();
            r.unlock();
        }
        
        
    }

    以后再补充

  • 相关阅读:
    java8新特性之Lambda表达式入门
    小结
    Kafka入门
    关于java多线程初试
    关于Netty入门
    IOS UITableView代码添加数据源和指定委托
    C#读书笔记1
    vs2008 C# Windows Mobile 智能设备开发 初步1
    Microsoft ActiveSync简介(来自网络)
    winForm单击用户区可移动窗体,代码控制窗体最大适中
  • 原文地址:https://www.cnblogs.com/wpbing/p/10429695.html
Copyright © 2011-2022 走看看