外语网站建设目的,微信公众平台小程序助手,建设网站的优势,wordpress 调试插件下载多线程 1、 方式一 Thread2、实现Runnable接口3、实现 Callable接口4、与线程有关的操作方法5、线程安全问题5.1 取钱案例5.2 线程同步5.2.1 同步代码块5.2.2 同步方法5.2.3 Lock锁 6、线程池6.2 创建线程池6.2.1 使用ExecutorService创建新任务策略6.2.2 使用Executors工具类创… 多线程 1、 方式一 Thread2、实现Runnable接口3、实现 Callable接口4、与线程有关的操作方法5、线程安全问题5.1 取钱案例5.2 线程同步5.2.1 同步代码块5.2.2 同步方法5.2.3 Lock锁 6、线程池6.2 创建线程池6.2.1 使用ExecutorService创建新任务策略6.2.2 使用Executors工具类创建核心线程数量问题 7、并发、并行线程生命周期7、乐观锁、悲观锁 1、 方式一 Thread
继承 Thread类 优点编码简单 缺点java是单继承继承了一个类就不能继承其他可拓展性不强。
注意: ①子线程一定要调用start方法而不是run方法调用run方法还是相当于在main线程中创建了一个实例对象在运行她的方法 而已是单线程的。调用strat方法虽然底层还是调用run方法但是他会告诉cpu说我们另外启动了一个线程。 ②子线程启动一定要在主线程任务之前。 ③每次执行的结果会不同。
package com.cky.file;public class MyThread extends Thread{Overridepublic void run() {for (int i 0; i 5 ; i) {System.out.println(子线程i);}}
}
package com.cky.file;public class Endecode {public static void main(String[] args) throws Exception {Thread threadnew MyThread();thread.start();for (int i 0; i 5 ; i) {System.out.println(主线程i);}}}
2、实现Runnable接口 package com.cky.file;public class MyRunnable implements Runnable{Overridepublic void run() {for (int i 0; i 5 ; i) {System.out.println(子线程i);}}
}
package com.cky.file;public class Endecode {public static void main(String[] args) throws Exception {//创建一个任务对象MyRunnable myRunnablenew MyRunnable();//调用线程类的start方法new Thread(myRunnable).start();//匿名内部类Runnable runnable new Runnable() {Overridepublic void run() {for (int i 0; i 5; i) {System.out.println(子线程 i);}}};new Thread(runnable).start();//简化形式1new Thread(new Runnable() {Overridepublic void run() {for (int i 0; i 5; i) {System.out.println(子线程 i);}}}).start();//形式2new Thread(()- {for (int i 0; i 5; i) {System.out.println(子线程 i);}}).start();for (int i 0; i 5 ; i) {System.out.println(主线程i);}}}
由于Runnable是一个函数式接口匿名内部类可以使用lambda形式
3、实现 Callable接口
上边两种实现方法都不能获得线程执行的结果并返回方法3可以。 实现Callable接口 将该接口 封装为一个FutureTask 任务对象最后在交给线程。 是一个泛型接口 这里String 是要返回的类型 可以定义为其他类型
package com.cky.file;import java.util.concurrent.Callable;public class MyCallable implements CallableString {private int n;public MyCallable(int n) {this.n n;}Overridepublic String call() throws Exception {int sum0;for (int i 1; i n; i) {sumi;}return 1-n的和为:sum;}
}
package com.cky.file;import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class Endecode {public static void main(String[] args) throws Exception {//创建一个Callable对象CallableString callablenew MyCallable(100);//封装为一个FutureTask对象//FutureTask 是一个任务对象 实现了Runnable接口// 在执行完毕后 可以通过get方法获得执行结果FutureTaskString f1 new FutureTask(callable);//交给一个Thread对象new Thread(f1).start();//获得执行结果 注意执行未结束 是不会取得结果的String s f1.get();System.out.println(s);//1-100的和为:5050}} 4、与线程有关的操作方法 package com.cky.file;public class MyThread extends Thread{public MyThread( String name){super(name);}Overridepublic void run() {Thread tThread.currentThread();for (int i 0; i 3 ; i) {System.out.println(t.getName()i);}}
}
package com.cky.file;import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class Endecode {public static void main(String[] args) throws Exception {MyThread myThread1new MyThread(子线程1);//修改名字
// myThread1.setName(子线程1);myThread1.start();MyThread myThread2new MyThread(子线程2);
// myThread2.setName(子线程2);myThread2.start();//哪个线程在执行 就是哪个线程Thread m Thread.currentThread();String name m.getName();System.out.println(name);for (int i 0; i 3; i) {System.out.println(name线程i);}}}
package com.cky.file;import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class Endecode {public static void main(String[] args) throws Exception {
//当输出为3时 延缓5秒在运行for (int i 0; i 5; i) {System.out.println(i);if(i3)Thread.sleep(5000);}}}
package com.cky.file;import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class Endecode {public static void main(String[] args) throws Exception {MyThread myThread1new MyThread(子线程1);myThread1.start();myThread1.join();//当前线程执行完毕 再往下进行MyThread myThread2new MyThread(子线程2);myThread2.start();myThread2.join();Thread m Thread.currentThread();String name m.getName();System.out.println(name);for (int i 0; i 3; i) {System.out.println(name线程i);}}}
子线程10 子线程11 子线程12 子线程20 子线程21 子线程22 main main线程0 main线程1 main线程2
5、线程安全问题
5.1 取钱案例
造成线程安全问题原因 同时存在多个线程访问同一个共享资源且都要修改该共享资源。 同时进入钱够的判断造成线程 不安全
package com.cky.file;public class Account {private double money;public Account(double money) {this.money money;}public double getMoney() {return money;}public void setMoney(double money) {this.money money;}public void drawmoney(double money){String nameThread.currentThread().getName();if (this.moneymoney){System.out.println(name来取钱成功);this.money-money;System.out.println(name取后剩余:this.money);}else{System.out.println(钱不够);}}
}
package com.cky.file;public class MyThread extends Thread{private Account account;public MyThread( Account account,String name){super(name);this.accountaccount;}Overridepublic void run() {
//取钱account.drawmoney(10000);}}
package com.cky.file;import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class Endecode {public static void main(String[] args) {//创建一个共享账户Account accountnew Account(10000);//两个线程MyThread myThread1new MyThread(account,小红);MyThread myThread2new MyThread(account,小明);myThread1.start();myThread2.start();}}
小红来取钱成功 小明来取钱成功 小红取后剩余:0.0 小明取后剩余:-10000.0
5.2 线程同步
解决线程安全问题的办法
5.2.1 同步代码块 public void drawmoney(double money){String nameThread.currentThread().getName();synchronized (this) {if (this.moneymoney){System.out.println(name来取钱成功);this.money-money;System.out.println(name取后剩余:this.money);}else{System.out.println(钱不够);}}
实例方法 同步代码块 通常加this 代表当前资源 比如小红和小黑共享一个账户 小白和小亮共享一个账户使用this 保证了 只锁住同一个账户 不被多个访问 但不会去锁住别人的账户。 如果是静态方法 每个类只有一份 只允许一个访问 通常用 类.class 来锁住所有只允许一个人访问。 public static void text(){synchronized (Account.class){}}5.2.2 同步方法 public synchronized void drawmoney(double money){String nameThread.currentThread().getName();if (this.moneymoney) {System.out.println(name 来取钱成功);this.money - money;System.out.println(name 取后剩余: this.money);}else{System.out.println(钱不够);}}5.2.3 Lock锁 package com.cky.file;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Account {private double money;//为每个账户创建一个锁对象private final Lock locknew ReentrantLock();public Account(double money) {this.money money;}public double getMoney() {return money;}public void setMoney(double money) {this.money money;}public void drawmoney(double money){String nameThread.currentThread().getName();//加锁lock.lock();try {if (this.moneymoney) {System.out.println(name 来取钱成功);this.money - money;System.out.println(name 取后剩余: this.money);}else{System.out.println(钱不够);}} catch (Exception e) {e.printStackTrace();}finally {//写在finally里为了防止上边代码出错导致没有解锁lock.unlock();}}
}
6、线程池
如果每一个线程都需要我们去新创建一个线程的话就会耗资源并且耗时(创建线程是一件耗时的事 此时就需要线程池线程池可以复用线程不用经常去创建线程。 并且线程池也可以确定任务队列中任务的个数。防止任务过多导致内存溢出。
6.2 创建线程池 6.2.1 使用ExecutorService创建 package com.cky.file;public class MyRunnable implements Runnable{private int n;public MyRunnable(int n) {this.n n;}Overridepublic void run() {int sum0;for (int i 1; i n; i) {sumi;}System.out.println(Thread.currentThread().getName()-1-n的和为:sum);try {Thread.sleep(100000);} catch (InterruptedException e) {e.printStackTrace();}}
}package com.cky.file;import java.util.concurrent.*;public class Endecode {public static void main(String[] args) {//创建一个线程池对象/* int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueueRunnable workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler*/ExecutorService poolnew ThreadPoolExecutor(3,5,8,TimeUnit.SECONDS,new ArrayBlockingQueue(4),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());MyRunnable myRunnablenew MyRunnable(100);pool.execute(myRunnable);pool.execute(myRunnable);pool.execute(myRunnable);//开始往任务队列加pool.execute(myRunnable);pool.execute(myRunnable);pool.execute(myRunnable);pool.execute(myRunnable);//开始使用临时线程pool.execute(myRunnable);pool.execute(myRunnable);
// 开始使用任务队列满的措施pool.execute(myRunnable);}}D:\JAVA\jdk-17.0.8\bin\java.exe -javaagent:D:\SOftware\idea\IntelliJ IDEA 2021.3.2\lib\idea_rt.jar49289:D:\SOftware\idea\IntelliJ IDEA 2021.3.2\bin -Dfile.encodingUTF-8 -classpath E:\java_code\project\out\production\hello-app;E:\java_code\project\hello-app\lib\dom4j-2.1.4.jar;E:\java_code\project\hello-app\lib\commons-io-2.15.1.jar com.cky.file.Endecode
Exception in thread main java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask5b480cf9[Not completed, task java.util.concurrent.Executors$RunnableAdapter5f184fc6[Wrapped task com.cky.file.MyRunnable3feba861]] rejected from java.util.concurrent.ThreadPoolExecutor6f496d9f[Running, pool size 5, active threads 5, queued tasks 4, completed tasks 0]at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2065)at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:833)at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1365)at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:123)at com.cky.file.Endecode.main(Endecode.java:31)
pool-1-thread-1-1-100的和为:5050
pool-1-thread-2-1-100的和为:5050
pool-1-thread-5-1-100的和为:5050
pool-1-thread-3-1-100的和为:5050
pool-1-thread-4-1-100的和为:5050
//我们可以看到 使用了临时线程 并且使用了任务满时的策略。
新任务策略 package com.cky.file;import java.util.concurrent.Callable;public class MyCallable implements CallableString {private int n;public MyCallable(int n) {this.n n;}Overridepublic String call() throws Exception {int sum0;for (int i 1; i n; i) {sumi;}return Thread.currentThread().getName()-1-n的和为:sum;}
}
package com.cky.file;import java.util.concurrent.*;public class Endecode {public static void main(String[] args) throws ExecutionException, InterruptedException {//创建一个线程池对象/* int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueueRunnable workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler*/ExecutorService poolnew ThreadPoolExecutor(3,5,8,TimeUnit.SECONDS,new ArrayBlockingQueue(4),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());FutureString f1 pool.submit(new MyCallable(100));FutureString f2 pool.submit(new MyCallable(200));FutureString f3 pool.submit(new MyCallable(300));FutureString f4 pool.submit(new MyCallable(400));System.out.println(f1.get());System.out.println(f2.get());System.out.println(f3.get());System.out.println(f4.get());}}
pool-1-thread-1-1-100的和为:5050 pool-1-thread-2-1-200的和为:20100 pool-1-thread-3-1-300的和为:45150 pool-1-thread-3-1-400的和为:80200
6.2.2 使用Executors工具类创建 创建单个线程池 ExecutorService pool Executors.newSingleThreadExecutor();pool.execute(new MyRunnable(100));
使用该种方式的风险
核心线程数量问题
如果时IO密集型 则一般配置 cpu数量*2 如果是计算密集型 则一般是 cpu数量1
7、并发、并行线程生命周期 并发 并行 生命周期
7、乐观锁、悲观锁
悲观锁一上来就加锁线程安全性能较差。 乐观锁不加锁等到要开始出现线程安全问题时才开始控制。
package com.cky.file;import java.util.concurrent.atomic.AtomicInteger;public class MyRunnable implements Runnable{private AtomicInteger countnew AtomicInteger();Overridepublic void run() {for (int i 0; i 100 ; i) {System.out.println(Thread.currentThread().getName()countcount.incrementAndGet());}}
}
package com.cky.file;import java.util.concurrent.*;public class Endecode {public static void main(String[] args) throws ExecutionException, InterruptedException {//乐观锁//100个线程 对一个变量 各加100次MyRunnable myRunnablenew MyRunnable();for (int i 0; i 100 ; i) {new Thread(myRunnable).start();}}}