线程使用说明(Thread):

文章详细介绍了Java中创建线程的三种方式:继承Thread类、实现Runnable接口和实现Callable接口,并对比了各自的优缺点。接着,文章讨论了线程安全问题,提供了同步代码块、同步方法和Lock锁作为解决方案。最后,文章阐述了线程池的概念和重要性,展示了如何使用ThreadPoolExecutor创建线程池并执行任务,以提高系统效率和资源管理。
摘要由CSDN通过智能技术生成

一、创建方式:继承Thread、实现Runnable接口、实现Callable接口

1.实现类继承Thread类:通过实现类继承Thread类重新Run方法,从而创建Thread对象完成线程创建,注意启动线程一定是调用线程实现类对象的start方法

弊端:Java因为是单继承,所以通过这种方式创建线程对象,拓展性较差

public class MyThread extends Thread{ // 2、必须重写Thread类的run方法 @Override public void run() { // 描述线程的执行任务。 for (int i = 1; i <= 5; i++) { System.out.println("子线程MyThread输出:" + i); } } } //测试类 public class ThreadTest1 { // main方法是由一条默认的主线程负责执行。 public static void main(String[] args) { // 3、创建MyThread线程类的对象代表一个线程 Thread t = new MyThread(); // 4、启动线程(自动执行run方法的) t.start(); for (int i = 1; i <= 5; i++) { System.out.println("主线程main输出:" + i); } } }

2.实现Runnable接口:通过实现类继承Runnable重新Run方法,从而创建Thread对象完成线程创建,注意启动线程一定是调用线程实现类对象的start方法

PS:相对于第一种,通过接口可轻松实现功能拓展,且可以通过匿名内部类的方式创建线程对象,灵活性更好

/** * 1、定义一个任务类,实现Runnable接口 */ public class MyRunnable implements Runnable{ // 2、重写runnable的run方法 @Override public void run() { // 线程要执行的任务。 for (int i = 1; i <= 5; i++) { System.out.println("子线程输出 ===》" + i); } } } public class ThreadTest2 { public static void main(String[] args) { // 3、创建任务对象。 Runnable target = new MyRunnable(); // 4、把任务对象交给一个线程对象处理。 // public Thread(Runnable target) new Thread(target).start(); for (int i = 1; i <= 5; i++) { System.out.println("主线程main输出 ===》" + i); } } }

2.实现Callable接口:此种方式跟上面两种有一定区别,创建方式如下:

1.先定义一个Callable接口的实现类,重写call方法

2.创建Callable实现类的对象

3.创建FutureTask类的对象,将Callable对象传递给FutureTask

4.创建Thread对象,将Future对象传递给Thread

5.调用Thread的start()方法启动线程(启动后会自动执行call方法)

等call()方法执行完之后,会自动将返回值结果封装到FutrueTask对象中

6.调用FutrueTask对的get()方法获取返回结果

PS:此种方式使用于线程运行过程中需要返回值的场景

/** * 1、让子类继承Thread线程类。 */ public class MyThread extends Thread{ // 2、必须重写Thread类的run方法 @Override public void run() { // 描述线程的执行任务。 for (int i = 1; i <= 5; i++) { System.out.println("子线程MyThread输出:" + i); } } } public class ThreadTest3 { public static void main(String[] args) throws Exception { // 3、创建一个Callable的对象 Callable<String> call = new MyCallable(100); // 4、把Callable的对象封装成一个FutureTask对象(任务对象) // 未来任务对象的作用? // 1、是一个任务对象,实现了Runnable对象. // 2、可以在线程执行完毕之后,用未来任务对象调用get方法获取线程执行完毕后的结果。 FutureTask<String> f1 = new FutureTask<>(call); // 5、把任务对象交给一个Thread对象 new Thread(f1).start(); Callable<String> call2 = new MyCallable(200); FutureTask<String> f2 = new FutureTask<>(call2); new Thread(f2).start(); // 6、获取线程执行完毕后返回的结果。 // 注意:如果执行到这儿,假如上面的线程还没有执行完毕 // 这里的代码会暂停,等待上面线程执行完毕后才会获取结果。 String rs = f1.get(); System.out.println(rs); String rs2 = f2.get(); System.out.println(rs2); } }

二、线程安全

说明:多个线程同时操作同一个共享资源的时候,可能会出现业务安全问题。

PS:用银行共享账户举例,若一对夫妻有一个共同账户10000元,那么在丈夫取钱(A线程)的同时,妻子也进行取钱操作(B线程),两人同时取10000元,那么可能会出现,A线程取钱未完成的时候,B线程也进入取钱流程,这样会出现两人都能取出10000元,导致账户剩余-10000元。

解决方案:那么如何避免上述情况发生,这里就引入了线程同步问题,若我们让线程可以依次执行,那么上述情况即可解决,即:在A线程执行时,其余线程均等待它执行完成。

方法1、:同步代码块

作用:将线程需要访问的共享资源进行上锁,同一时间只允许一个线程执行操作,以此保证线程安全

代码:

//锁对象:必须是一个唯一的对象(同一个地址) synchronized(锁对象){ //...访问共享数据的代码... } // 小明 小红线程同时过来的 public void drawMoney(double money) { // 先搞清楚是谁来取钱? String name = Thread.currentThread().getName(); // 1、判断余额是否足够 // this正好代表共享资源! synchronized (this) { if(this.money >= money){ System.out.println(name + "来取钱" + money + "成功!"); this.money -= money; System.out.println(name + "来取钱后,余额剩余:" + this.money); }else { System.out.println(name + "来取钱:余额不足~"); } } }

PS:锁对象可以是任意对象(不安全),一般采用this

方法2、:同步方法

说明:与同步代码块相似,就是把整个方法给锁住,一个线程调用这个方法,另一个线程调用的时候就执行不了,只有等上一个线程调用结束,下一个线程调用才能继续执行。

代码:

// 同步方法 public synchronized void drawMoney(double money) { // 先搞清楚是谁来取钱? String name = Thread.currentThread().getName(); // 1、判断余额是否足够 if(this.money >= money){ System.out.println(name + "来取钱" + money + "成功!"); this.money -= money; System.out.println(name + "来取钱后,余额剩余:" + this.money); }else { System.out.println(name + "来取钱:余额不足~"); } }

方法3:线程锁(Lock)

说明:Lock锁是JDK5版本专门提供的一种锁对象,通过这个锁对象的方法来达到加锁,和释放锁的目的,使用起来更加灵活。

代码:

1.首先在成员变量位子,需要创建一个Lock接口的实现类对象(这个对象就是锁对象) private final Lock lk = new ReentrantLock(); 2.在需要上锁的地方加入下面的代码 lk.lock(); // 加锁 //...中间是被锁住的代码... lk.unlock(); // 解锁 // 创建了一个锁对象 private final Lock lk = new ReentrantLock(); public void drawMoney(double money) { // 先搞清楚是谁来取钱? String name = Thread.currentThread().getName(); try { lk.lock(); // 加锁 // 1、判断余额是否足够 if(this.money >= money){ System.out.println(name + "来取钱" + money + "成功!"); this.money -= money; System.out.println(name + "来取钱后,余额剩余:" + this.money); }else { System.out.println(name + "来取钱:余额不足~"); } } catch (Exception e) { e.printStackTrace(); } finally { lk.unlock(); // 解锁 } } }

三、线程池技术

说明:线程池技术就是线程复用技术,那么为什么要使用呢,核心原因就是节省资源,当我们使用线程对象来执行时,没有任何限制,任何一个功能需要,就可能要创建一个线程,若同一时间线程数过大可能会导致整体运行效率下降,这时候就用到了线程池,线程池会限制系统最多能创建的线程数以及等待创建线程的队列,这样就避免了问题,

如何创建:线程池的接口ExecutorService,而这个接口下有一个实现类叫ThreadPoolExecutor类,使用ThreadPoolExecutor类就可以用来创建线程池对象。(推荐使用)

ExecutorService pool = new ThreadPoolExecutor( 3, //核心线程数有3个 5, //最大线程数有5个。 临时线程数=最大线程数-核心线程数=5-3=2 8, //临时线程存活的时间8秒。 意思是临时线程8秒没有任务执行,就会被销毁掉。 TimeUnit.SECONDS,//时间单位(秒) new ArrayBlockingQueue<>(4), //任务阻塞队列,没有来得及执行的任务在,任务队列中等待 Executors.defaultThreadFactory(), //用于创建线程的工厂对象 new ThreadPoolExecutor.CallerRunsPolicy() //拒绝策略 );

线程池执行Runnable任务:

ExecutorService pool = new ThreadPoolExecutor( 3, //核心线程数有3个 5, //最大线程数有5个。 临时线程数=最大线程数-核心线程数=5-3=2 8, //临时线程存活的时间8秒。 意思是临时线程8秒没有任务执行,就会被销毁掉。 TimeUnit.SECONDS,//时间单位(秒) new ArrayBlockingQueue<>(4), //任务阻塞队列,没有来得及执行的任务在,任务队列中等待 Executors.defaultThreadFactory(), //用于创建线程的工厂对象 new ThreadPoolExecutor.CallerRunsPolicy() //拒绝策略 ); Runnable target = new MyRunnable(); pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的! pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的! pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的! //下面4个任务在任务队列里排队 pool.execute(target); pool.execute(target); pool.execute(target); pool.execute(target); //下面2个任务,会被临时线程的创建时机了 pool.execute(target); pool.execute(target); // 到了新任务的拒绝时机了! pool.execute(target);

线程池执行Callable任务:

public class ThreadPoolTest2 { public static void main(String[] args) throws Exception { // 1、通过ThreadPoolExecutor创建一个线程池对象。 ExecutorService pool = new ThreadPoolExecutor( 3, 5, 8, TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy()); // 2、使用线程处理Callable任务。 Future<String> f1 = pool.submit(new MyCallable(100)); Future<String> f2 = pool.submit(new MyCallable(200)); Future<String> f3 = pool.submit(new MyCallable(300)); Future<String> f4 = pool.submit(new MyCallable(400)); // 3、执行完Callable任务后,需要获取返回结果。 System.out.println(f1.get()); System.out.println(f2.get()); System.out.println(f3.get()); System.out.println(f4.get()); } }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值