多线程的实现
java.lang.Thread类代表多线程
注意事项
- 启动线程必须是start方法,不是调用run方法
- 不要把主线任务放在启动子线程之前
继承Thread
/**
* @author Pickle
* @version V1.0
* @date 2024/3/11 16:43
*/
public class MyThread extends Thread{
@Override
public void run() {
....
}
}
优点:编码简单
缺点:线程类已经继承Thread,无法继承其他类,不利于功能扩展
实现Runable接口
import java.lang.management.RuntimeMXBean;
/**
* @author Pickle
* @version V1.0
* @date 2024/3/11 16:43
*/
public class MyThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("子线程" + i);
}
}
}
/**
* @author Pickle
* @version V1.0
* @date 2024/3/11 16:50
*/
public class ThreadDemo {
public static void main(String[] args) {
Runnable myThread = new MyThread();
new Thread(myThread).start();
for (int i = 0; i < 10;i ++){
System.out.println("主线程" + i);
}
}
}
优点:人物类只是实现接口,可以继续继承其他类、实现其他接口,扩展性强
缺点:需要多一个Runnable对象
上述方法也可以使用匿名内部类实现
/**
* @author Pickle
* @version V1.0
* @date 2024/3/11 16:55
*/
public class ThreadDemo {
public static void main(String[] args) {
new Thread(()-> {
for (int i = 0; i < 10; i++) {
System.out.println("子线程" + i);
}
}).start();
for (int i = 0; i < 10; i++){
System.out.println("主线程" + i);
}
}
}
实现Callable接口
这种线程创建方式最大的优点就是可以返回线程执行的结果。
- 实现Callable接口
- 重写call方法
import java.util.concurrent.Callable;
/**
* @author Pickle
* @version V1.0
* @date 2024/3/11 17:01
*/
public class MyCallable implements Callable<String> {
private int n;
public MyCallable(int n){
this.n = n;
}
@Override
public String call() throws Exception {
//描述现成的任务和返回线程执行后的结果
int sum = 0;
for (int i = 1; i <= n;i++){
sum += i;
}
return "线程求出了1-"+n+"的和为:" + sum;
}
}
- 创建MyCallable对象
- 创建FurtureTack对象
- 创建线程并且Start
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author Pickle
* @version V1.0
* @date 2024/3/11 16:55
*/
public class ThreadDemo {
public static void main(String[] args) {
Callable<String> myCallable = new MyCallable(100);
//未来任务对象是一个任务对象,实现了Runnable接口
//可以在线程执行完毕之后,用未来任务对象调用get方法获取线程执行完毕之后的结果
final FutureTask<String> ft = new FutureTask<>(myCallable);
new Thread(ft).start();
for (int i = 0; i < 10; i++) {
System.out.println("主线程" + i);
}
//获取线程执行之后的结果
try {
//如果执行到这里,线程的结果还没计算完毕,会wait
System.out.println(ft.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
线程的安全问题
多个线程同时操作一个数据,可能会造成数据修改异常,造成错误的结果。
线程同步
锁的范围越小,性能越好
- 同步代码块
代码块上锁
- 同步方法
方法上锁
- Lock锁
private final Lock lk = new ReentrantLock();
使用Lock一定要使用try/catch/finally,最后解锁,否则会造成死锁,确保早必要时释放锁。
线程通信(同步)
线程同步的前提一定是先要保证线程安全
生产者与消费者
import java.util.ArrayList;
import java.util.List;
/**
* @author Pickle
* @version V1.0
* @date 2024/3/11 18:11
*/
public class ThreadDemo {
public static void main(String[] args) {
Desk desk = new Desk();
new Thread(()->{
while (true) {
desk.put();
}
},"厨师1").start();
new Thread(()->{
while (true) {
desk.put();
}
},"厨师2").start();
new Thread(()->{
while (true) {
desk.put();
}
},"厨师3").start();
new Thread(()->{
while (true) {
desk.get();
}
},"吃货1").start();
new Thread(()->{
while (true) {
desk.get();
}
},"吃货2").start();
}
}
class Desk{
private List<String> list = new ArrayList<>();
//厨师123
public synchronized void put(){
try {
final String name = Thread.currentThread().getName();
if(list.isEmpty()){
list.add(name + "做的肉包子");
System.out.println(name + "做了一个肉包子");
Thread.sleep(2000);
this.wait();
this.notifyAll();
}else{
this.wait();
this.notifyAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//消费者12
public synchronized void get(){
try {
final String name = Thread.currentThread().getName();
if (!list.isEmpty()){
System.out.println(name + "吃了" + list.get(0));
list.clear();
Thread.sleep(2000);
this.wait();
this.notifyAll();
}else{
this.wait();
this.notifyAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程池
可复用的线程技术
JDK5.0起提供了代表线程池的接口:ExecutorService
得到线程池的两种方式
- 使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象
临时线程什么时候创建?
新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
线程池处理Runnable任务
/**
* @author Pickle
* @version V1.0
* @date 2024/3/11 22:11
*/
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "==> 输出");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
import java.util.concurrent.*;
/**
* @author Pickle
* @version V1.0
* @date 2024/3/11 18:46
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
/*new ThreadPoolExecutor( int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)*/
ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(4),Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
Runnable target = new MyRunnable();
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
pool.execute(target);
}
}
线程池处理Callable任务
import java.util.concurrent.*;
/**
* @author Pickle
* @version V1.0
* @date 2024/3/11 18:46
*/
public class ThreadPoolDemo {
public static void main(String[] args) throws Exception {
ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(4),Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
final Future<String> f1 = pool.submit(new MyCallable(100));
final Future<String> f2 = pool.submit(new MyCallable(200));
final Future<String> f3 = pool.submit(new MyCallable(300));
final Future<String> 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());
}
}
- 使用Executors(线程池工具类)调用方法返回不同特点的线程池对象
import java.util.concurrent.*;
/**
* @author Pickle
* @version V1.0
* @date 2024/3/11 18:46
*/
public class ThreadPoolDemo {
public static void main(String[] args) throws Exception {
final ExecutorService ft = Executors.newFixedThreadPool(3);
final Future<String> f1 = ft.submit(new MyCallable(100));
final Future<String> f2 = ft.submit(new MyCallable(200));
final Future<String> f3 = ft.submit(new MyCallable(300));
final Future<String> f4 = ft.submit(new MyCallable(400));
System.out.println(f1.get());
System.out.println(f2.get());
System.out.println(f3.get());
System.out.println(f4.get());
}
}
大型并发系统中,如果使用Executors如果不注意可能会出现系统风险
线程状态
悲观锁、乐观锁
悲观锁:一上来就加锁,每次只能一个线程进入访问完毕之后,再解锁。------线程安全,性能较差
标签:Thread,System,线程,println,new,多线程,public From: https://www.cnblogs.com/poteitoutou/p/18067349乐观锁:一开始不上锁,等要出现线程安全问题的时候才开始控制。------线程安全,性能好