多线程基础0:多线程创建方式

实现多线程的方式(runnable与Thread)

Thread 和 Runnable 的相同点:都是“多线程的实现方式”。

Thread 和 Runnable 的不同点:

Thread 是类,而Runnable是接口;Thread本身是实现了Runnable接口的类。我们知道“一个类只能有一个父类,

但是却能实现多个接口”,因此Runnable具有更好的扩展性。

此外,Runnable还可以用于“资源的共享”。即,多个线程都是基于某一个Runnable对象建立的,

它们会共享Runnable对象上的资源。通常,建议通过“Runnable”实现多线程!

runable的方式来创建多线程

package com.example;
public class MyThread implements Runnable{
    private int ticket=10;
    @Override
    public void run() {
        for (int i=0;i<20;i++){
            if(this.ticket>0){
                System.out.println(Thread.currentThread().getName()+" sale ticket "+this.ticket--);
            }
        }
    }
}

package com.example;
public class Test {
    public static void main(String[] args)throws InterruptedException {
        MyThread myThread=new MyThread();
        Thread t1=new Thread(myThread);
        Thread t2=new Thread(myThread);
        Thread t3=new Thread(myThread);
        t1.start();
        t2.start();
        t3.start();
    }
}

结果
三个线程共享一个runable对象
Thread-1 sale ticket 9
Thread-1 sale ticket 7
Thread-1 sale ticket 6
Thread-1 sale ticket 5
Thread-1 sale ticket 4
Thread-1 sale ticket 3
Thread-2 sale ticket 8
Thread-2 sale ticket 1
Thread-1 sale ticket 2
Thread-0 sale ticket 10
View Code
package com.example;
public class MyThread implements Runnable{
    private int ticket=10;
    @Override
    public void run() {
        for (int i=0;i<20;i++){
            if(this.ticket>0){
                try {//这里休眠1s
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" sale ticket "+this.ticket--);
            }
        }
    }
}
结果
Thread-0 sale ticket 10
Thread-1 sale ticket 9
Thread-2 sale ticket 8
Thread-0 sale ticket 7
Thread-2 sale ticket 6
Thread-1 sale ticket 5
Thread-0 sale ticket 4
Thread-2 sale ticket 3
Thread-1 sale ticket 2
Thread-0 sale ticket 1
Thread-1 sale ticket 0
Thread-2 sale ticket -1
View Code

说明这个是线程不同步的,在Thread-2读取值之后,Thread-1将值减到了0,而Thread-2还是会继续运行。

Thread实现多线程

package com.example;
public class MyThread extends  Thread{
    private int ticket=10;
    @Override
    public void run() {
        for (int i=0;i<20;i++){
            if(this.ticket>0){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" sale ticket "+this.ticket--);
            }
        }
    }
}

package com.example;
public class Test {
    public static void main(String[] args)throws InterruptedException {
        MyThread t1=new MyThread();
        MyThread t2=new MyThread();
        MyThread t3=new MyThread();
        t1.start();
        t2.start();
        t3.start();
    }
}
View Code

Thread中start与run的区别

start() : 它的作用是启动一个新线程,新线程会执行相应的run()方法。start()不能被重复调用。

run() : run()就和普通的成员方法一样,可以被重复调用。单独调用run()的话,会在当前线程中执行run(),而并不会启动新线程!

new 一个 Thread,线程进入了新建状态;调用 start() 方法,会启动一个线程并使线程进入了就绪状态,

当分配到时间片后就可以开始运行了。

start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。

  • 而直接执行 run() 方法,会把 run 方法当成一个 main 线程下的普通方法去执行,
  • 并不会在某个线程中执行它,所以这并不是多线程工作。
  • 总结:
  • 调用 start 方法方可启动线程并使线程进入就绪状态,
  • 而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行

实现多线程方式1:继承Thread类,作为线程对象存在(继承Thread对象)

public class CreatThreadDemo1 extends Thread{
    /**
     * 构造方法: 继承父类方法的Thread(String name);方法
     * @param name
     */
    public CreatThreadDemo1(String name){
        super(name);
    }

    @Override
    public void run() {
        while (!interrupted()){
            System.out.println(getName()+"线程执行了...");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        CreatThreadDemo1 d1 = new CreatThreadDemo1("first");
        CreatThreadDemo1 d2 = new CreatThreadDemo1("second");

        d1.start();
        d2.start();

        d1.interrupt();  //中断第一个线程
    }
}
View Code

常规方法,不多做介绍了,interrupted方法,是来判断该线程是否被中断。

终止线程不允许用stop方法,该方法不会施放占用的资源。

所以我们在设计程序的时候,要按照中断线程的思维去设计,就像上面的代码一样。

让线程等待的方法

Thread.sleep(200); //线程休息2ms

Object.wait(); //让线程进入等待,直到调用Object的notify或者notifyAll时,线程停止休眠

实现多线程方式2:实现runnable接口,作为线程任务存在

public class CreatThreadDemo2 implements Runnable {
    @Override
    public void run() {
        while (true){
            System.out.println("线程执行了...");
        }
    }
    public static void main(String[] args) {
        //将线程任务传给线程对象
        Thread thread = new Thread(new CreatThreadDemo2());
        //启动线程
        thread.start();
    }
}
View Code

Runnable 只是来修饰线程所执行的任务,它不是一个线程对象。想要启动Runnable对象,必须将它放到一个线程对象里。

实现所线程方式3:匿名内部类创建线程对象

public class CreatThreadDemo3 extends Thread{
    public static void main(String[] args) {
        //创建无参线程对象
        new Thread(){
            @Override
            public void run() {
                System.out.println("线程执行了...");
            }
        }.start();
       //创建带线程任务的线程对象
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程执行了...");
            }
        }).start();
        //创建带线程任务并且重写run方法的线程对象
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("runnable run 线程执行了...");
            }
        }){
            @Override
            public void run() {
                System.out.println("override run 线程执行了...");
            }
        }.start();
    }

}
View Code

创建带线程任务并且重写run方法的线程对象中,为什么只运行了Thread的run方法。我们看看Thread类的源码

我们可以看到Thread实现了Runnable接口,而Runnable接口里有一个run方法。

所以,我们最终调用的重写的方法应该是Thread类的run方法。而不是Runnable接口的run方法。

实现多线程方式4:创建带返回值的线程

public class CreatThreadDemo4 implements Callable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CreatThreadDemo4 demo4 = new CreatThreadDemo4();

        FutureTask<Integer> task = new FutureTask<Integer>(demo4); //FutureTask最终实现的是runnable接口

        Thread thread = new Thread(task);

        thread.start();

        System.out.println("我可以在这里做点别的业务逻辑...因为FutureTask是提前完成任务");
        //拿出线程执行的返回值
        Integer result = task.get();
        System.out.println("线程中运算的结果为:"+result);
    }

    //重写Callable接口的call方法
    @Override
    public Object call() throws Exception {
        int result = 1;
        System.out.println("业务逻辑计算中...");
        Thread.sleep(3000);
        return result;
    }
}
Callable接口介绍:

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}
View Code

返回指定泛型的call方法。然后调用FutureTask对象的get方法得道call方法的返回值。

多线程实现方式5:定时器Timer

public class CreatThreadDemo5 {

    public static void main(String[] args) {
        Timer timer = new Timer();

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时器线程执行了...");
            }
        },0,1000);   //延迟0,周期1s

    }
}
View Code

实现多线程方式6:线程池创建线程

public class CreatThreadDemo6 {
    public static void main(String[] args) {
        //创建一个具有10个线程的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        long threadpoolUseTime = System.currentTimeMillis();
        for (int i = 0;i<10;i++){
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"线程执行了...");
                }
            });
        }
        long threadpoolUseTime1 = System.currentTimeMillis();
        System.out.println("多线程用时"+(threadpoolUseTime1-threadpoolUseTime));
        //销毁线程池
        threadPool.shutdown();
        threadpoolUseTime = System.currentTimeMillis();
    }
}
View Code

实现多线程方式7:利用java8新特性 stream 实现并发

lambda表达式不懂的,可以看看我的java8新特性文章:

java8-lambda:

https://www.jianshu.com/p/3a08dc78a05f

java8-stream:

https://www.jianshu.com/p/ea16d6712a00

public class CreatThreadDemo7 {
    public static void main(String[] args) {
        List<Integer> values = Arrays.asList(10,20,30,40);
        //parallel 平行的,并行的
        int result = values.parallelStream().mapToInt(p -> p*2).sum();
        System.out.println(result);
        //怎么证明它是并发处理呢
        values.parallelStream().forEach(p-> System.out.println(p));
    }
}
View Code

多线程的创建与线程安全

实现多线程的两种方式

继承Thread类,重写Thread类中的run方法

public class MyThread extends Thread{    
    @Override
    public void run(){
        super.run();
        System.out.println("this is myThread run");
    }
}
View Code
public static void main(String[] args) {    
    Thread myThread=new MyThread();
    myThread.start();
    System.out.println("mian function is over");
}
View Code

注意:代码的顺序并不是线程的执行顺序,start的顺序也不是多个线程的执行顺序。

实现Runable接口

java是单继承的,一个子类只能去继承一个父类,所以如果线程类已经继承了其他父类,那么就不能采用继承thread类来实现多线程了

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("this is myrunable running");
    }
}
View Code
public class Test {
    public static void main(String[] args) {        
        Runnable myRunnable=new MyRunnable();
        Thread thread=new Thread(myRunnable);
        thread.start();
        System.out.println("main is runing");
    }
}
View Code

 实例变量与线程安全

数据不共享的实例,两个线程中分别创建了两个thread实例,各种都是独立的count

public class MyThread extends Thread{    
    private int count=5;    
    public MyThread(String name) {
        super();
        this.setName(name);
    }    
    @Override
    public void run(){
        super.run();
        while(count>0){
            count--;
            System.out.println(this.currentThread().getName());
            System.out.println(count);
        }
    }
}
View Code
public class Test {
    public static void main(String[] args) {            
        Thread thread1=new MyThread("A");
        Thread thread2=new MyThread("B");
        thread1.start();
        thread2.start();    
    }
}
View Code

数据共享的实例

public class MyThread extends Thread {
    private int count = 5;
    @Override
    public void run() {
        super.run();
        count--;
        System.out.println(this.currentThread().getName()+count);
    }
}

public class Test {
    public static void main(String[] args) {            
        Thread thread=new MyThread();
        
        Thread thread1=new Thread(thread, "A");
        Thread thread2=new Thread(thread,"B");
        Thread thread3=new Thread(thread, "C");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
View Code

可以在方法上加同步锁,syschronized,这样每一个线程在执行这个方法前需要先尝试去获取这把锁,获取不到就会等待,一直到获取到。

public class MyThread extends Thread {
    private int count = 5;
    @Override
    synchronized public void run() {
        super.run();
        count--;
        System.out.println(this.currentThread().getName()+count);
    }
}
View Code

下面是一个多线程非安全对的共享变量实例:

在实例中共享了静态变量usanameRef和passwordRef

public class Alogin extends Thread{
    @Override
    public void run(){
        LoginServlet.doPost("a", "aa");
    }
}
public class Blogin extends Thread{    
    @Override
    public void run(){
        LoginServlet.doPost("b", "bb");
    }
}
public class LoginServlet {
    private static String usenameRef;
    private static String passwordRef;
  synchronized public static void doPost(String usename,String password){
        try {
            usenameRef=usename;
            
            if(usename.equals("a")){
                Thread.sleep(2000);
            }
            passwordRef=password;
            System.out.println("username="+usenameRef+"password="+password);            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class Test {
    public static void main(String[] args) {                
        Alogin alogin=new Alogin();
        alogin.start();
        Blogin blogin=new Blogin();
        blogin.start();
    }
}
View Code

参考文章:

Java多线程系列--“基础篇”02之 常用的实现多线程的两种方式

并发基础篇(五): 创建线程的四种方式

并发基础篇(六):深入线程Thread类的start()方法和run()方法

Java多线程系列--“基础篇”02之 常用的实现多线程的两种方式

Java多线程系列--“基础篇”03之 Thread中start()和run()的区别

Java 多线程(二)—–创建与启动

参考: 《java多线程编程核心技术》

https://blog.csdn.net/iaiti/article/details/53314149

 

posted @ 2021-06-08 19:53  弱水三千12138  阅读(55)  评论(0)    收藏  举报