1、java中实现多线程
方法一
在java中负责线程的这个功能的是java.lang.Thread这个类
可以通过创建Thread的实例来创建新的线程
每个线程都是通过某个特定的Thread对象所对应的方法run()来完成其操作,方法run()称为线程体
通过调用Thread类的start()方法来启动一个线程
/** * 实现多线程的时候 * 1、需要继承Thread类 * 2、必须重写run方法,指的是核心执行逻辑 * 3、线程在启动的时候,不要直接调用run()方法,而是要通地start()来进行调用 * 4、每次运行相同的代码,出来的结果可能不一样,原因在于多线程谁先抢占资源无法进行人为控制 */ public class Test extends Thread { @Override public void run() { for(int i = 0; i< 20; i++) { System.out.println(Thread.currentThread().getName() + "--------------" + i); } } public static void main(String[] args) { Test t = new Test(); t.start(); for(int i = 0; i<10; i++) { System.out.println(Thread.currentThread().getName() + "==============" + i); } } }
方法二
/** * 实现多线程的时候 * 1、实现Runnable接口 * 2、必须重写run方法,指的是核心执行逻辑 * 3、创建Thread对象,将刚刚创建好的Runnable子类实现作Thread的构造函数 * 4、通过thread的实例化对象调用start()方法进行调用启动 */ public class Test implements Runnable { @Override public void run() { for(int i = 0; i< 20; i++) { System.out.println(Thread.currentThread().getName() + "--------------" + i); } } public static void main(String[] args) { Runnable t = new Test(); Thread th = new Thread(t); th.start(); //需要先启动进程,这样再执行其他代码才会并行 for(int i = 0; i<10; i++) { System.out.println(Thread.currentThread().getName() + "==============" + i); } } }
注意:推荐使用第二种方法,在使用runnable接口后不需要给共享变量添加static关键字, 每次创建一个对象,作为共享对象即可, 同时可以避免单继承,把extends留给更需要的继承的地方
2、线程的生命周期:
1、新生状态:
当创建好当前线程对象之后,没有启动之前(调用start方法之前)
ThreadDemo thread = new ThreadDemo()
RunnableDemo run = new RunnableDemo()
2、就绪状态:准备开始执行,并没有执行,表示调用start方法之后
当对应的线程创建完成,且调用start方法之后,所有的线程会添加到一个就绪队列中,所有的线程同时去抢占cpu的资源
3、运行状态:当当前进程获取到cpu资源之后,就绪队列中的所有线程会去抢占cpu的资源,谁先抢占到谁先执行,在执行的过程中就叫做运行状态
抢占到cpu资源,执行代码逻辑开始
4、死亡状态:当运行中的线程正常执行完所有的代码逻辑或者因为异常情况导致程序结束叫做死亡状态
进入的方式:
1、正常运行完成且结束
2、人为中断执行,比如使用stop方法
3、程序抛出未捕获的异常
5、阻塞状态:在程序运行过程中,发生某些异常情况,导致当前线程无法再顺利执行下去,此时会进入阻塞状态,进入阻塞状态的原因消除之后,
所有的阻塞队列会再次进入到就绪状态中,随机抢占cpu的资源,等待执行
进入的方式:
sleep方法
等待io资源
join方法(代码中执行的逻辑)
注意:
在多线程的时候,可以实现唤醒和等待的过程,但是唤醒和等待操作的对应不是thread类
而是我们设置的共享对象或者共享变量
多线程并发访问的时候回出现数据安全问题:
解决方式:
1、同步代码块
synchronized(共享资源、共享对象,需要是object的子类){具体执行的代码块}
2、同步方法
将核心的代码逻辑定义成一个方法,使用synchronized关键字进行修饰,此时不需要指定共享对象
3、进程操作的相关函数
public class Test implements Runnable { @Override public void run() { for(int i = 0; i< 20; i++) { System.out.println(Thread.currentThread().getName() + "--------------" + i); } } public static void main(String[] args) { Runnable t = new Test(); Thread th = new Thread(t); th.start(); //需要先启动进程,这样再执行其他代码才会并行 //获取指定进程的名称 System.out.println(th.getName()); //获取指定进程的id System.out.println(th.getId()); //获取指定进程的优先级,默认是5,值越大,被执行的可能性就越大 System.out.println(th.getPriority()); //设置指定进程的优先级,通常是1-10, Thread.MAX_PRIORITY Thread.MIN_PRIORITY Thread.NORM_PRIORITY th.setPriority(10); //获取指定进程的描述 System.out.println(th.getState()); //判断指定进程是否存活 System.out.println(th.isAlive()); //获取当前进程即哪个先运行,那么打印的就是哪个进程 System.out.println(Thread.currentThread()); for(int i = 0; i<10; i++) { System.out.println(Thread.currentThread().getName() + "==============" + i); } } }
public class Test implements Runnable { @Override public void run() { for(int i = 0; i< 10; i++) { System.out.println(Thread.currentThread().getName() + "-----------"+ i); } } public static void main(String[] args) { Runnable r = new Test(); Thread th = new Thread(r); th.start(); // th.stop(); //直接终止线程,不推荐使用 try { // th.join(); //相当指定线程强制执行,其他线程处于阻塞状态,该线程执行完成后,其他线程再继续执行 // System.out.println(th.getName()); Thread.sleep(1000); //表示在哪个线程里运行的,那么那个线程就进行休眠 } catch (InterruptedException e) { e.printStackTrace(); } for(int i = 0; i< 5; i++) { System.out.println(Thread.currentThread().getName() + "==============" + i); if(i == 0) { Thread.yield(); //表示当前正在执行的线程暂停一次,允许其他线程执行,不阻塞,线程进入就绪状态,如果没有其他等待的程序,这时候当前程序就会立马恢复执行 System.out.println(Thread.currentThread().getName() + "休息一回合"); } } } }
注意:这里的Thread.sleep与Thread.yield两个方法是Thread的静态方法,在哪个线程体内调用,那么这两个方法就作用在哪个线程中;通俗的讲反正在哪一个线程里面执行了sleep()或yield()方法哪一个线程就休眠或暂停。
注意:
在多线程的时候,可以实现唤醒和等待的过程,但是唤醒和等待操作的对应不是thread类
而是我们设置的共享对象或者共享变量
多线程并发访问的时候回出现数据安全问题:
解决方式:
1、同步代码块
synchronized(共享资源、共享对象,需要是object的子类){具体执行的代码块}
2、同步方法以及同步静态方法
将核心的代码逻辑定义成一个方法,使用synchronized关键字进行修饰,此时不需要指定共享对象
线程对应的例子
//创建一个Goods接口 public interface IGoods { public void produce(); public void consumer(); }
//创建一个商品,并且实现商品接口 public class WaterGoods implements IGoods { private String name; private int amount; public WaterGoods() {} public WaterGoods(String name) { this.name = name; } @Override public synchronized void produce() { this.amount ++; this.notify(); System.out.println("生产一个单位的" + this.name + ", 现在的数量是" + this.amount); } @Override public synchronized void consumer() { if(this.amount < 1){ try { this.wait(); //如果商品还没有生产出来,那么就需要进行等待 } catch (InterruptedException e) { e.printStackTrace(); } } this.amount --; System.out.println("消费一个单位的" + this.name + ", 现在数量是" + this.amount); } }
//创建一个生产类,并且实现线程的接口 public class ProduceThread implements Runnable { private final IGoods goods; private final int num; public ProduceThread(IGoods goods, int num) { this.goods = goods; this.num = num; } @Override public void run() { for(int i = 0; i < num; i ++) { this.goods.produce(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
//创建一个消费类,并且实现线程接口 public class ConsumerThread implements Runnable{ private final IGoods goods; private final int num; public ConsumerThread(IGoods goods, int num) { this.goods = goods; this.num = num; } @Override public void run() { for(int i = 0; i < num; i ++) { this.goods.consumer(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
//创建和启动线程 public class Test { public static void main(String[] args) { IGoods water = new WaterGoods("矿泉水"); IGoods cake = new CakeGoods("面包"); Thread pTh = new Thread(new ProduceThread(water, 10)); Thread cTh = new Thread(new ConsumerThread(water, 10)); pTh.start(); cTh.start(); } }
4、BlockingQueue类的使用
具体查看相关博客: https://blog.csdn.net/qq_42135428/article/details/80285737
相应的例子
public class Goods { private String brand; private String type; public Goods() {} public Goods(String brand, String type) { this.brand = brand; this.type = type; System.out.println("生产了" + this.toString()); } @Override public String toString() { return "【商品名称: "+ this.brand + ", 商品种类: " + this.type + "】"; } }
public class Produce implements Runnable{ private final BlockingQueue<Goods> queue; public Produce (BlockingQueue<Goods> queue) { this.queue = queue; } @Override public void run() { for(int i = 0; i< 20; i++) { try { Goods goods = new Goods("康师傅", "冰红茶"); this.queue.put(goods); System.out.println("生产了" + goods.toString() + "现在的数量是" + this.queue.size()); } catch (InterruptedException e) { e.printStackTrace(); } } } }
public class Consume implements Runnable { private final BlockingQueue<Goods> queue; public Consume (BlockingQueue<Goods> queue) { this.queue = queue; } @Override public void run() { for(int i = 0; i< 18; i++) { try { Goods goods = this.queue.take(); System.out.println("消费了" + goods.toString() + "现在余下" + this.queue.size()); } catch (InterruptedException e) { e.printStackTrace(); } } } }
//在调用take与put的时候,类的内部已做了等待
public class Test { public static void main(String[] args) { BlockingQueue<Goods> goodsList = new ArrayBlockingQueue<>(10); Thread pth = new Thread(new Produce(goodsList)); Thread cth = new Thread(new Consume(goodsList)); pth.start(); cth.start(); } }
5、线程池分类
ThreadPollExecutor的使用
public class Task implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + "---running"); } }
public class Test { public static void main(String[] args) { //创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 ExecutorService executorService = Executors.newCachedThreadPool(); //创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 ExecutorService executorService = Executors.newFixedThreadPool(10); //创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行 ExecutorService executorService = Executors.newSingleThreadExecutor(); for(int i = 0; i < 2000; i ++) { executorService.execute(new Task()); } executorService.shutdown(); } }
ScheduledExecutorService的使用
public class Test { public static void main(String[] args) { ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5); //表示延时固定的时间后执行一次 scheduledExecutorService.schedule(new Task(), 3, TimeUnit.SECONDS); //表示循环的延时固定的时间执行一次,下面代码表示,每三秒,延时一秒执行一次 scheduledExecutorService.scheduleAtFixedRate(new Task(), 1, 3, TimeUnit.SECONDS); //该方法与上个方法的区别在于,该方法表示上个任务执行完成后再进行延迟,而上个方法表示在上个任务开始时进行计时 scheduledExecutorService.scheduleWithFixedDelay(new Task(), 1, 3, TimeUnit.SECONDS); // scheduledExecutorService.shutdown(); //这里注意要注释掉 } }
注意:Executors.newScheduleThreadPool与Executors.newSingleThreadScheduledExecutor的区别在于后者是单线程操作
Lombok的基本使用