多线程实现

36 篇文章 1 订阅

多线程原理

相当于玩游戏机只有一个游戏机(cpu),可是有很多人要玩,于是,start是排队!等CPU选中你就是轮到你,你就run(),当CPU的运行的时间片执行完,这个线程就继续排队等待下一次的run()

调用start()后,线程会被放到等待队列等待CPU调度,并不一定要马上开始执行只是将这个线程置于可动行状态

然后通过JVM,线程Thread会调用run()方法执行本线程的线程体

先调用start后调用run,这么麻烦,为了不直接调用run?就是为了实现多线程的优点,没这个start不行
多线程就是分时利用CPU,宏观上让所有线程一起执行 ,也叫并发

start() 和 run()

1 start()方法来 启动线程 真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码;通过调用Thread类的start()方法来启动一个线程, 这时此线程是处于就绪状态并没有运行。 然后通过此Thread类调用方法run()来完成其运行操作的, 这里方法run()称为线程体,它包含了要执行的这个线程的内容Run方法运行结束, 此线程终止。然后CPU再调度其它线程

2 run()方法当作普通方法的方式调用。程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码; 程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的。

class Runner1 implements Runnable { // 实现了Runnable接口,jdk就知道这个类是一个线程  
   @Override
   public void run() {  
       for (int i = 0; i < 100; i++) {  
           System.out.println("进入Runner1运行状态——————————" + i);  
       }  
   }  
}  
 
class Runner2 implements Runnable { // 实现了Runnable接口,jdk就知道这个类是一个线程  
   @Override
   public void run() {  
       for (int i = 0; i < 100; i++) {  
           System.out.println("进入Runner2运行状态==========" + i);  
       }  
   }  
}
public class test0 {  
    public static void main(String[] args) {  
        Runner1 runner1 = new Runner1();  
        Runner2 runner2 = new Runner2();  
//	      Thread(Runnable target) 分配新的 Thread 对象。  
        Thread thread1 = new Thread(runner1);  
        Thread thread2 = new Thread(runner2);  
        thread1.run();  
        thread2.run();  
    }  
}  

这时的运行结果是,线程1执行完毕后,线程2才执行,因为这里并没有给线程排队,而是直接调用的run()方法,相当于调用了一个普通的函数一样, thread1.run()和thread2.run()顺序执行

public class test0 {  
    public static void main(String[] args) {  
        Runner1 runner1 = new Runner1();  
        Runner2 runner2 = new Runner2();  
//	      Thread(Runnable target) 分配新的 Thread 对象。  
        Thread thread1 = new Thread(runner1);  
        Thread thread2 = new Thread(runner2);  
        thread1.start();  
        thread2.start();  
    }  
}  

当线程使用start()方法时,就会出现两个线程的run()方法交替执行的状态
在这里插入图片描述

常见的几种方法

创建线程的4种方式 必看

继承Thread类重写run()方法

Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法

public class MyThread extends Thread{
    @OverWrite
    public void run(){
        //编写自己的线程代码
        for (int i = 0; i < 5; i++) {
            Thread.sleep(100);
            System.out.println("Thread name : " + Thread.currentThread().getName());
        }
    }
}

public class ThreadTest {
    public static void main(String[] args){
       MyThread myThread1 = new MyThread();
       myThread1.setName("0");
	   MyThread myThread2 = new MyThread();
	   myThread2.setName("1");
	   
	   myThread1.start();
	   myThread2.start();
    }
}

在这里插入图片描述

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        ThreadDemo myThread1 = new ThreadDemo();
        myThread1.setName("0");
	    MyThread myThread2 = new MyThread();
	    myThread2.setName("1");
	    
	    myThread1.start();
	    myThread2.start();
    }
}

class ThreadDemo extends Thread {

    @SneakyThrows
    @Override
    public void run() {
        Thread.sleep(100);
        boolean flag = false;
        for (int i = 3; i < 20; i++) {
            flag = false;
            for (int j = 2; j <= Math.sqrt(i); j++) {
                if (i % j == 0) {
                    flag = true;
                    break;
                }
            }
            if (flag == false) {
                System.out.println(Thread.currentThread().getName() + "->" + i);
            }
        }
    }
}

在这里插入图片描述

public class ThreadTest {
    public static void main(String[] args) {
        new Thread("thread1") {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "->" + i);
                }
            }
        }.start();

        new Thread("thread2") {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "->" + i);
                }
            }
        }.start();
    }
}

在这里插入图片描述

实现Runnable接口,重写run()方法

如果自己的类已经extends另一个类,就无法直接extends Thread,此时,必须实现一个Runnable接口

public class MyThread extends OtherClass implements Runnable {
  public void run() {
   	System.out.println("MyThread.run()");
  }
}

为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例

public class ThreadDemo02 {

    public static void main(String[] args){ 
        MyThread myThread = new MyThread();
	    new Thread(myThread , "0").start();
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "->" + i);
                }
            }
        }, "thread1").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "->" + i);
                }
            }
        }, "thread2").start();
    }
}

实现Callable接口,重写call()方法,通过FutureTask包装器来创建线程

a: 创建Callable接口的实现类 ,并实现Call方法
b: 创建Callable实现类的实现,使用FutureTask类包装Callable对象,该FutureTask对象封装了Callable对象的Call方法的返回值
c: 使用FutureTask对象作为Thread对象的target创建并start()线程
d: 调用FutureTask对象的get()获取子线程执行结束的返回值

public class ThreadDemo03 {
    public static void main(String[] args) {
        Callable myCallable = new MyCallable<>();
        FutureTask futureTask = new FutureTask<>(myCallable);

        new Thread(oneTask).start();
        System.out.println(futureTask.get());
    }
}

class MyCallable implements Callable<Integer>{

    //重写call方法
    @Override
    public Integer call() throws Exception {
        // TODO Auto-generated method stub
        System.out.println(Thread.currentThread().getName()+"-->我是通过实现Callable接口通过FutureTask包装器来实现的线程");
        return 0;
    }   
}

这种方式需要借助FutureTask类来支持。优于方法1、2的一点是可以添加返回值,并且可以抛出异常

但是需要注意的一点是,如果需要获取返回值result.get()这个方法是阻塞的。所以你在创建线程和调用result.get()方法之间没有耗时操作 或者 没有其他线程 效果和顺序执行没有区别,还不如不创建线程。

public class TestCallable1 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
    	CallableDemo callableDemo = new CallableDemo();
    	FutureTask futureTask = new FutureTask<>(callableDemo); 
    	new Thread(futureTask).start();
    	List<Integer> lists = (List<Integer>)futureTask.get(); //获取返回值
    	for (Integer integer : lists) {
			System.out.print(integer + "  ");
		}
	}
}
 
class CallableDemo implements Callable<List<Integer>>{
 
	@Override
	public List<Integer> call() throws Exception {
		List<Integer> lists = new ArrayList<>();
		for(int i  = 0 ; i <= 10 ; i ++) {
			if(i%2==0) {
				lists.add(i);
			}
		}
		return lists;
	}	
}

使用线程池创建线程

ExecutorService、Callable、Future这个对象实际上都是属于Executor框架中的功能类

可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须实现Runnable接口

执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了,再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了

public class RunnableDemo implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }

    public static void main(String[] args) {
        // 创建一个固定大小的线程池:
        ExecutorService es = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 6; i++) {
            es.submit(new RunnableDemo());
        }
        // 关闭线程池:
        es.shutdown();
    }
}
public class ThreadDemo05{

    private static int POOL_NUM = 10;     //线程池数量

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(5);  
        for(int i = 0; i < POOL_NUM; i++){  
            RunnableThread thread = new RunnableThread();
            //Thread.sleep(1000);
            executorService.execute(thread);  
        }
        //关闭线程池
        executorService.shutdown(); 
    }   
}

class RunnableThread implements Runnable {     
    @Override
    public void run(){  
        System.out.println("通过线程池方式创建的线程:" + Thread.currentThread().getName() + " ");  
    }  
}  

newFixedThreadPool 创建固定大小数量线程池,数量通过传入的参数决定

newSingleThreadExecutor 创建一个线程容量的线程池所有的线程依次执行,相当于创建固定数量为1的线程池

newCachedThreadPool 创建可缓存的线程池没有最大线程限制(实际上是Integer.MAX_VALUE)。如果用空闲线程等待时间超过一分钟,就关闭该线程

newScheduledThreadPool 创建计划(延迟)任务线程池,线程池中的线程可以让其在特定的延迟时间之后执行,也可以以固定的时间重复执行(周期性执行)。相当于以前的Timer类的使用

newSingleThreadScheduledExecutor 创建单线程池延迟任务,创建一个线程容量的计划任务

工作中学到

springboot 多线程实现

springboot 多线程实现

多线程可能带来的问题

多线程常见问题汇总
多线程可能带来的问题

同步与并发问题

并发:在同一时刻,有多个线程同时访问 某一个(一些)资源,带来数据的不安全性 、不稳定性、不确定性。

生活中例子
下课时,多个同学同时抢占同一坑位。
同步:用于解决并发问题,给予线程权限,允许具有权限的线程执行。

实现同步方法-

性质:唯一(static)

使用原则:锁越少越好 ,避免死锁发生

实现方法:

1.synchronized

2.Lock Condition

3.synchronized 方法名() {} :锁就是当前对象

多线程的同步:countdownlatch 
生活中例子
坑位有锁,需锁开启。下课时,多个同学抢占同一坑位,仅有一位同学—甲有钥匙(锁即权限),在甲开锁(获取锁)占坑后,锁坑门(同步)。在甲完成如厕动作期间,其他同学仅能在厕所外等待。

执行权限
CPU通过时间片给予线程执行权限及时间,具有权限的线程才能执行

经典面试题

线程run方法执行顺序

线程执行start()的时候,会先执行自己的run()方法,只有当自己的run()为空的时候,才会去执行父类的runnable
new Thread(
        new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("1");
                }
            }
        }
) {
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("2");
        }
    }
}.start();

我们把上面代码精简一下,可以得到如下代码:

 new Thread(runnable.run()){   //只有当自己的run()为空的时候才会被执行
 	 @OverWrite  
     run()    //自己的run第一个被执行
 }.start();

在这里插入图片描述

target(Runnable)不为空的时候,run方法才会被执行

首先 Thread执行start()的时候,会先去执行自己的run()方法只有当自己的run()为空的时候,才会去执行父类的runnable,因此上文被执行的是System.out.println("2"); 父类此时被子类覆盖了
在这里插入图片描述

如何一个类既继承了Thread类,又实现了Runnable接口。运行start方法的时候是执行重写Thread类里面的run()方法还是执行重写Runnable接口里面的run()方法?如下程序输出什么?

 public static void main(String[] args) {
     new Thread(()-> System.out.println("Runnable")){
         @Override
         public void run() {
             System.out.println("Thread");
         }
     }.start();
 }
 
输出的是 Thread
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值