多线程
Posted 杨杨杨杨杨杨杨振
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程相关的知识,希望对你有一定的参考价值。
创建和启动线程的方式一
- 自定义类继承Thread类并重写run方法,然后创建该类的对象调用start方法。
创建和启动线程的方式二
- 自定义类实现Runnable接口并重写run方法,创建该类的对象作为实参来构造Thread类型的对象,然后使用Thread类型的对象调用start方法。
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
System.out.println("t1");
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("t2");
}
}).start();
new Thread(()->{
System.out.println("t3");
}).start();
}
1.继承方式管理线程编号和名称
public class ThreadIdNameTest extends Thread{
public ThreadIdNameTest(String sun) {
//调用父类的构造方法
super(sun);
}
@Override
public void run() {
System.out.println("子线程的ID:" + this.getId());
System.out.println("子线程的名称:" + getName());
}
public static void main(String[] args) {
ThreadIdNameTest t1 = new ThreadIdNameTest("sun");
t1.start();
System.out.println("主线程的ID:" + Thread.currentThread().getId());
System.out.println("主线程的名称:" + Thread.currentThread().getName());
}
}
===========
主线程的ID:1
子线程的名称:sun
主线程的名称:main
2.接口的方式管理线程编号和名称
public class MainTest {
public static void main(String[] args) {
new Thread(()->{
System.out.println("子线程的ID:" + Thread.currentThread().getId());
System.out.println("子线程的名称:" + Thread.currentThread().getName());
Thread.currentThread().setName("tttt");
System.out.println("子线程的名称:" + Thread.currentThread().getName());
}).start();
System.out.println("主线程的ID:" + Thread.currentThread().getId());
System.out.println("主线程的名称:" + Thread.currentThread().getName());
}
}
===========
子线程的ID:14
主线程的ID:1
子线程的名称:Thread-0
主线程的名称:main
子线程的名称:tttt
3.常用的方法
Sleep
public class ThreadSleepTest extends Thread {
private boolean flag = true;
@Override
public void run() {
while (flag) {
LocalDateTime now = LocalDateTime.now();
String time = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:ss:mm"));
System.out.println(time);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ThreadSleepTest t1 = new ThreadSleepTest();
t1.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.flag = false;
}
}
守护线程
public class ThreadDaemonTest extends Thread{
@Override
public void run() {
//默认不是守护线程
//当子线程不是守护线程时,虽然主线程先结束了,但是子线程依然会继续执行,直到打印所有数据为止
//当子线程是守护时,当主线程结束后,则子线程随之结束
for(int i = 0;i < 50;i++){
System.out.println("子线程:i = "+ i);
}
}
public static void main(String[] args) {
ThreadDaemonTest t1 = new ThreadDaemonTest();
//必须在线程启动之前设置子线程为守护线程
t1.setDaemon(true);
t1.start();
for(int i = 0;i < 20;i++){
System.out.println("主线程:i = "+ i);
}
}
}
join等待该线程终止
/**
* @Author 振帅
* @Date 2021/05/26 21:49
*/
public class ThreadJoinTest {
public static void main(String[] args) {
//偶数
Thread t1 = new Thread(()->{
for(int i = 0;i < 100;i += 2){
System.out.println("ti==:" + i);
}
});
//奇数
Thread t2 = new Thread(()->{
for(int i = 1;i < 100;i += 2){
System.out.println("t2:" + i);
}
});
System.out.println("主线程开始");
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程结束");
}
}
线程同步机制
1.同步代码块实现线程同步方式一
- synchronized (dm)
/**
* @Author 振帅
* @Date 2021/05/26 22:39
* synchronized 模拟取钱
*/
public class AccountRunnableTest implements Runnable{
class Demo { }
//锁
Demo dm = new Demo();
//账户余额
private int balance;
public AccountRunnableTest(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public void run() {
System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
//synchronized (new Demo()){ //锁不住
synchronized (dm){
//1.模拟从后台查询余额
int temp = getBalance();
//2.模拟取款200
if (temp > 200) {
System.out.println("正在出钞,请稍后...");
temp -= 200;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("请取走你的钞票!");
} else {
System.out.println("余额不足");
}
//3.更新余额
setBalance(temp);
}
}
public static void main(String[] args) {
AccountRunnableTest account = new AccountRunnableTest(1000);
Thread t1 = new Thread(account);
Thread t2 = new Thread(account);
t1.start();
t2.start();
System.out.println("主线程开始等待...");
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("最终账户余额为:"+ account.getBalance());
}
}
2.同步代码块实现线程同步方式二
- static 修饰锁
/**
* @Author 振帅
* @Date 2021/05/26 22:39
* synchronized 模拟取钱
*/
public class AccountThreadTest extends Thread{
static class Demo { }
//锁 属于类层级 所有对象共享一个
private static Demo dm = new Demo();
//账户余额
private int balance;
public AccountThreadTest(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public void run() {
System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
synchronized (dm){
//1.模拟从后台查询余额
int temp = getBalance();
//2.模拟取款200
if (temp > 200) {
System.out.println("正在出钞,请稍后...");
temp -= 200;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("请取走你的钞票!");
} else {
System.out.println("余额不足");
}
//3.更新余额
setBalance(temp);
}
}
public static void main(String[] args) {
AccountThreadTest at1 = new AccountThreadTest(1000);
AccountThreadTest at2 = new AccountThreadTest(1000);
at1.start();
at2.start();
System.out.println("主线程开始等待...");
try {
at1.join();
at2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("at1账户余额为:"+ at1.getBalance());
System.out.println("at2账户余额为:"+ at2.getBalance());
}
}
3.同步方法块实现线程同步方式一
- 使用synchronized锁住整个方法
/**
* @Author 振帅
* @Date 2021/05/26 22:39
* synchronized 模拟取钱
*/
public class AccountRunnableTest implements Runnable{
//账户余额
private int balance;
public AccountRunnableTest(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public synchronized void run() {
System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
//1.模拟从后台查询余额
int temp = getBalance();
//2.模拟取款200
if (temp > 200) {
System.out.println("正在出钞,请稍后...");
temp -= 200;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("请取走你的钞票!");
} else {
System.out.println("余额不足");
}
//3.更新余额
setBalance(temp);
}
public static void main(String[] args) {
AccountRunnableTest account = new AccountRunnableTest(1000);
Thread t1 = new Thread(account);
Thread t2 = new Thread(account);
t1.start();
t2.start();
System.out.println("主线程开始等待...");
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("最终账户余额为:"+ account
.getBalance());
}
}
该方式等价于:
synchronized(this) { 整个方法体的代码 }
this 代表调用run方法的对象,由源码可知 调用run方法的对象是account
因此 synchronized(this){} 可以锁住
@Override
public void run() {
synchronized (this){
System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
//1.模拟从后台查询余额
int temp = getBalance();
//2.模拟取款200
if (temp > 200) {
System.out.println("正在出钞,请稍后...");
temp -= 200;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("请取走你的钞票!");
} else {
System.out.println("余额不足");
}
//3.更新余额
setBalance(temp);
}
}
4.同步方法块实现线程同步方式二
- 当我们对一个静态方法加锁,如:
public synchronized static void xxx(){….} - 那么该方法锁的对象是类对象。每个类都有唯一的一个类对象。获取类对象的方式:类名.class。
- 静态方法与非静态方法同时使用了synchronized后它们之间是非互斥关系的。
- 原因在于:静态方法锁的是类对象而非静态方法锁的是当前方法所属对象。
/**
* @Author 振帅
* @Date 2021/05/26 22:39
* synchronized
*/
public class AccountThreadTest extends Thread{
//账户余额
private int balance;
public AccountThreadTest(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
public synchronized static void test(){
System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
System.out.println("正在出钞,请稍后...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("请取走你的钞票!");
}
@Override
public void run() {
test();
}
public static void main(String[] args) {
AccountThreadTest at1 = new AccountThreadTest(1000);
AccountThreadTest at2 = new AccountThreadTest(1000);
at1.start();
at2.start();
System.out.println("主线程开始等待...");
try {
at1.join();
at2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("at1账户余额为:"+ at1.getBalance());
System.out.println("at2账户余额为:"+ at2.getBalance());
}
}
等价于
public static void test(){
synchronized(AccountThreadTest.class){
System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
System.out.println("正在出钞,请稍后...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("请取走你的钞票!");
}
}
5.使用Lock(锁)实现线程同步
- 从Java5开始提供了更强大的线程同步机制—使用显式定义的同步锁对象来实现。
- java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
- 该接口的主要实现类是ReentrantLock类,该类拥有与synchronized相同的并发性,在以后的线程安全控制中,经常使用ReentrantLock类显式加锁和释放锁。
与synchronized方式的比较
- Lock是显式锁,需要手动实现开启和关闭操作,而synchronized是隐式锁,执行锁定代码后自动
释放。 - Lock只有同步代码块方式的锁,而synchronized有同步代码块方式和同步方法两种锁。
- 使用Lock锁方式时,Java虚拟机将花费较少的时间来调度线程,因此性能更好。
/**
* @Author 振帅
* @Date 2021/05/26 22:39
* ReentrantLock 模拟取钱
*/
public class AccountRunnableTest implements Runnable{
//账户余额
private int balance;
//准备锁
private ReentrantLock lock = new ReentrantLock();
public AccountRunnableTest(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public void run() {
//开始加锁
lock.lock();
System.out.println("线程" + Thread.currentThread().getName() + "已启动...");
//1.模拟从后台查询余额
int temp = getBalance();
//2.模拟取款200
if (temp > 200) {
System.out.println("正在出钞,请稍后...");
temp -= 200;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("请取走你的钞票!");
} else {
System.out.println("余额不足");
}
//3.更新余额
setBalance(temp);
//解锁
lock.unlock();
}
public static void main(String[] args) {
AccountRunnableTest account = new AccountRunnableTest(1000);
Thread t1 = new Thread(account);
Thread t2 = new Thread(account);
t1.start();
t2.start();
System.out.println("主线程开始等待...");
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("最终账户余额为:"+ account.getBalance());
}
}
线程通信
1.Object类常用的方法
方法声明 | 功能介绍 |
---|---|
void wait() | 用于使得线程进入等待状态,直到其它线程调用notify()或notifyAll()方 法 |
void wait(long timeout) | 用于进入等待状态,直到其它线程调用方法或参数指定的毫秒数已经过去为止 |
void notify() | 用于唤醒等待的单个线程 |
void notifyAll() | 用于唤醒等待的所有线程 |
/**
* @Author 振帅
* @Date 2021/05/26 23:53
* 线程通信 两个线程交替打印1-100
*/
public class ThreadCommunicateTest implements Runnable {
private int cnt = 1;
@Override
public void run() {
while (true) {
synchronized (this){
//用于唤醒等待的单个线程 写在锁里面
notify();
if(cnt <= 100){
System.out.println("线程:"+ Thread.currentThread().getName() +"中的cnt:= " +cnt);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
cnt++;
try {
//用于使得线程进入等待状态,直到其它线程调用notify()或notifyAll()方法 写在锁里面
//当前线程打印完一个数 为了防止继续打印 调用wait()
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}
public static void main(String[] args) {
ThreadCommunicateTest tc = new ThreadCommunicateTest();
Thread t1 = new Thread(tc);
Thread t2 = new Thread(tc);
t1.start();
t2.start();
}
}
2.生产者消费者模型实现
仓库类
/**
* @Author 振帅
* @Date 2021/05/27 0:20
* 仓库类
*/
public class StoreHouse {
//记录产品的数量
private int cnt = 0;
//生产方法
public synchronized void produceProduct(){
notify();
if (cnt < 10) {
System.out.println("线程"+Thread.currentThread().getName()+"正在生产第"+(cnt+1)+"个产品");
cnt++;
} else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费方法
public synchronized void consumerProduct(){
notify();
if (cnt > 0) {
System.out.println("线程"+Thread.currentThread().getName()+"正在消费第"+(cnt)+"个产品");
cnt--;
} else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
生产者
/**
* @Author 振帅
* @Date 2021/05/27 0:22
* 生产者
*/
public class ProduceThread extends Thread{
//声明一个仓库类型的引用作为成员变量,为了能调用仓库类中的生产方法 合成复用原则
private StoreHouse storeHouse;
//为了确保两个线程共用一个仓库
public ProduceThread(StoreHouse storeHouse){
this.storeHouse = storeHouse;
}
@Override
public void run() {
while (true) {
storeHouse.produceProduct();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
消费者
/**
* @Author 振帅
* @Date 2021/05/27 0:22
* 消费者
*/
public class ConsumerThread extends Thread{
//声明一个仓库类型的引用作为成员变量,为了能调用仓库类中的生产方法 合成复用原则
private StoreHouse storeHouse;
//为了确保两个线程共用一个仓库
public ConsumerThread(StoreHouse storeHouse){
this.storeHouse = storeHouse;
}
@Override
public void run() {
while (true) {
storeHouse.consumerProduct();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试类
/**
* @Author 振帅
* @Date 2021/05/27 0:27
*/
public class StoreHouseTest {
public static void main(String[] args) {
//创建仓库类对象
StoreHouse storeHouse = new StoreHouse();
ProduceThread t1 = new ProduceThread(storeHouse);
ConsumerThread t2 = new ConsumerThread(storeHouse);
t1.start();
t2.start();
}
}
创建和启动线程的方式三
- 实现Callable接口
- FutureTask类 extends RunnableFuture extends Runnable
/**
* @Author 振帅
* @Date 2021/05/27 0:44
*/
public class ThreadCallableTest implements Callable<Integer> {
@Override
public Integer call() throws Exception {
//计算1-10000之间累加和
int sum = 0;
for (int i = 1;i <= 1000; i++){
sum = sum+i;
}
System.out.println("计算的结果为:" + sum);
return sum;
}
public static void main(String[] args) {
ThreadCallableTest tct = new ThreadCallableTest();
//用于描述可取消的异步计算,该类提供了Future接口的基本实
//现,包括启动和取消计算、查询计算是否完成以及检索计算结果的方法,也可以用于获取方法调用
//后的返回结果
FutureTask<Integer> task = new FutureTask<Integer>(tct);
Thread th = new Thread(task);
th.start();
Integer result = null;
try {
//获取call方法计算的结果
result = task.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("线程处理结果:" + result);
}
}
创建和启动线程的方式四
- Executors类
工具类和线程池的工厂类,可以创建并返回不同类型的线程池,常用方法如下:
方法声明 | 功能介绍 |
---|---|
static ExecutorService newCachedThreadPool() | 创建一个可根据需要创建新线程的线程池 |
static ExecutorService newFixedThreadPool(int nThreads) | 创建一个可重用固定线程数的线程池 |
static ExecutorService newSingleThreadExecutor() | 创建一个只有一个线程的线程池 |
- ExecutorService接口
真正的线程池接口,主要实现类是ThreadPoolExecutor,常用方法如下:
方法声明 | 功能介绍 |
---|---|
void execute(Runnable command) | 执行任务和命令,通常用于执行Runnable |
Future submit(Callable task) | 执行任务和命令,通常用于执行Callable |
void shutdown() | 启动有序关闭 |
public class ThreadPoolTest {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
ThreadCallableTest threadCallableTest = new ThreadCallableTest();
Future<Integer> future = executorService.submit(threadCallableTest);
Integer result = null;
try {
result = future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("线程处理结果:" + result);
executorService.shutdown();
}
}
以上是关于多线程的主要内容,如果未能解决你的问题,请参考以下文章