java多线程相关代码

2019-02-25 16:11:51来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

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();
    }
    
    
}

以后再补充


原文链接:https://www.cnblogs.com/wpbing/p/10429695.html
如有疑问请与原作者联系

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:玩转Spring Cloud之熔断降级(Hystrix)与监控

下一篇:Java 10 var关键字详解和示例教程