5-多线程详解【狂神】
线程简介
1、多任务
看似多个任务都在做,本质上我们大脑在同一时间依旧只做一个事情
2、多线程
普通方法调用:只有主线程一条执行路径
多条执行路径,主线程和子线程并行交替执行



线程实现(重点)
Thread

package Demo01;
//创建线程方式一:继承Thread类,重写run()方法,调用start()方法启动线程
//线程开程不一定按顺序执行,CPU会自动调度
public class TestThread1 extends Thread {
@Override
public void run() {
//run方法线程体
for(int i=0;i<10;i++) {
System.out.println("我在看代码" + i);
}
}
public static void main(String[] args) {
//main线程体
//怎么开启其它线程?
TestThread1 t1 = new TestThread1();
t1.start(); //启动线程
for(int i=0;i<10;i++) {
System.out.println("我在学习多线程" + i);
}
//执行start()方法后,main线程会继续执行,会交替执行,线程不一定按顺序执行,CPU安排调度
//我在看代码0
//我在学习多线程0
//我在看代码1
//我在学习多线程1
//我在看代码2
//我在学习多线程2
//我在看代码3
//我在学习多线程3
//我在看代码4
//我在学习多线程4
//我在看代码5
//我在学习多线程5
//我在看代码6
//我在学习多线程6
//我在看代码7
//我在学习多线程7
//我在看代码8
//我在学习多线程8
//我在看代码9
//我在学习多线程9
}
}
package Demo01;
import org.apache.commons.io.FileUtils;
//练习Thread类的使用,多线程同步下载图片
public class TestThread2 extends Thread {
private String url;//图片地址
private String Name;//图片名称
public TestThread2(String url, String Name) {
this.url = url;
this.Name = Name;
}
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.download(url, Name);
System.out.println(Name + "下载完成了!");
}
public static void main(String[] args) {
TestThread2 t1 = new TestThread2("https://pic.rmb.bdstatic.com/bjh/250208/dump/005c1358a976ac49792dac79961a4440.jpeg", "baidu1.png");
TestThread2 t2 = new TestThread2("https://pic.rmb.bdstatic.com/bjh/250208/dump/ff923d5da511b800065fa3fc471ebbf4.jpeg", "baidu2.png");
TestThread2 t3 = new TestThread2("https://pic.rmb.bdstatic.com/bjh/250208/dump/c72a0043dc181fcb3d38eb95ae461825.jpeg", "baidu3.png");
t1.start();
t2.start();
t3.start();
}
}
//先导入commons-io依赖,用于下载图片。可以到apache官网下载。
//下载器
class WebDownloader {
//下载方法
public void download(String url, String Name) {
try {
//下载图片
FileUtils.copyURLToFile(new java.net.URL(url), new java.io.File(Name));
System.out.println(Name + "下载完成!");
} catch (Exception e) {
e.printStackTrace();
System.out.println(Name + "下载失败!");
}
}
}
实现Runnable
package Demo01;
//创建线程方式2:实现Runnable接口,重写run()方法,执行线程需要丢入Runnable接口实现类,调用start()方法启动线程。
public class TestThread3 implements Runnable {
@Override
public void run() {
//run方法线程体
for(int i=0;i<10;i++) {
System.out.println("我在看代码" + i);
}
}
public static void main(String[] args) {
//创建runnable接口实现类的实例
TestThread3 t1 = new TestThread3();
//创建线程,并传入Runnable接口实现类的实例
Thread thread = new Thread(t1);
thread.start();
//简写成一行代码:Thread thread = new Thread(new TestThread3());
//启动线程
for (int i = 0; i < 10; i++) {
System.out.println("我在学习多线程" + i);
}
}
}
总结,推荐Runnable接口

初识并发问题
package Demo01;
//多个线程同时操作同一个对象
//买火车票的例子
//多个线程同时操作同一个对象,线程不安全,数据紊乱,也可能会出现一个人拿到重复的票
public class TestThread4 implements Runnable {
//票数
private int tickets = 100;
@Override
public void run() {
while(true){
if(tickets <= 0){
break;
}
System.out.println(Thread.currentThread().getName() + "拿到了第"+ tickets-- + "张票");//tickets--表示票数减一
}
}
public static void main(String[] args) {
TestThread4 ticket = new TestThread4();
new Thread(ticket, "小明").start();//此处用了简写的方式
new Thread(ticket, "老师").start();
new Thread(ticket, "黄牛").start();
}
}
实现Callable接口(了解即可)
package Demo01;
//线程创建三:实现Callable接口
//可以定义返回值
//可以抛出异常
//但是步骤复杂,
import org.apache.commons.io.FileUtils;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class TestCallable implements Callable<Boolean> {
private String url;//图片地址
private String Name;//图片名称
public TestCallable(String url, String Name) {
this.url = url;
this.Name = Name;
}
@Override
public Boolean call() {
WebDownloader1 webDownloader = new WebDownloader1();
webDownloader.download(url, Name);
System.out.println(Name + "下载完成了!");
return true;
}
public static void main(String[] args) throws Exception {
TestCallable t1 = new TestCallable("https://pic.rmb.bdstatic.com/bjh/250208/dump/005c1358a976ac49792dac79961a4440.jpeg", "baidu1.png");
TestCallable t2 = new TestCallable("https://pic.rmb.bdstatic.com/bjh/250208/dump/ff923d5da511b800065fa3fc471ebbf4.jpeg", "baidu2.png");
TestCallable t3 = new TestCallable("https://pic.rmb.bdstatic.com/bjh/250208/dump/c72a0043dc181fcb3d38eb95ae461825.jpeg", "baidu3.png");
//创建执行服务
ExecutorService executor = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> future1 = executor.submit(t1);
Future<Boolean> future2 = executor.submit(t2);
Future<Boolean> future3 = executor.submit(t3);
//获取结果
boolean result1 = future1.get();
boolean result2 = future2.get();
boolean result3 = future3.get();
//关闭执行服务
executor.shutdown();
}
}
//先导入commons-io依赖,用于下载图片。可以到apache官网下载。
//下载器
class WebDownloader1 {
//下载方法
public void download(String url, String Name) {
try {
//下载图片
FileUtils.copyURLToFile(new java.net.URL(url), new java.io.File(Name));
System.out.println(Name + "下载完成!");
} catch (Exception e) {
e.printStackTrace();
System.out.println(Name + "下载失败!");
}
}
}
静态代理
你:真实角色
婚庆公司:代理你,帮你处理结婚的事
结婚:实现都实现结婚接口即可
//静态代理模式总结
//真实对象和代理对象实现同一个接口
//代理对象要代理真实对象
//好处:代理对象可以做很多真实对象做不了的事情,真实对象专注做自己的事情
public class StacticProxy {
public static void main(String[] args) {
Marry target = new you();
WeddingCompany proxy = new WeddingCompany(target);
proxy.HappyMarry();
}
interface Marry {//定义接口
void HappyMarry();
}
//真实角色
static class you implements Marry {
@Override
public void HappyMarry() {
System.out.println("要结婚了");
}
}
//代理角色,帮助你结婚
static class WeddingCompany implements Marry {
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry();//调用真实角色的方法
after();
}
private void before() {
System.out.println("请安排婚礼仪式");
}
private void after() {
System.out.println("婚礼圆满结束");
}
}
}
Lambda表达式


package FF;
/**
* 推导lambda表达式
*/
public class TestLambda1 {
//3.静态内部类,可以将2简化到3
static class Like2 implements ILike {
public void lambda() {
System.out.println("I like lambda22!");
}
}
public static void main(String[] args) {
ILike like = new Like();
like.lambda();
ILike like2 = new Like2();
like2.lambda();
//4.局部内部类,可以将3简化到4
class Like3 implements ILike {
public void lambda() {
System.out.println("I like lambda33!");
}
}
ILike like3 = new Like3();
like3.lambda();
//5.匿名内部类,没有类的名称,必须借助接口或者父类,可以将4简化到5
like = new ILike() {
@Override
public void lambda() {
System.out.println("I like lambda44!");
}
};
like.lambda();
//6.lambda简化,可以将5简化到6
like = () -> System.out.println("I like lambda55!");//箭头后面是方法体
//love=(a,b,c)->{System.out.println(a+b+c);};//多参数的lambda表达式
like.lambda();
//I like lambda!
//I like lambda22!
//I like lambda33!
//I like lambda44!
//I like lambda55!
//总结:lambda表达式只能有一行代码的情况下,可以简化为更简洁的形式,但是如果有多行代码,那么就用代码块包裹
//前提接口为函数式接口,否则无法使用lambda表达式,函数式接口只有一个抽象方法
//多个参数也可以去掉参数类型,要去掉都去掉,必须加上括号
}
}
//1.定义一个函数式接口
interface ILike {
void lambda();
}
//2.实现类
class Like implements ILike {
public void lambda() {
System.out.println("I like lambda!");
}
}
线程状态



线程停止
package state;
//测试stop
//1.建议线程正常停止--》利用次数,不建议死循环
//2.建议使用标志位---》利用标志位,判断是否需要停止线程
//3.不要使用stop或destroy等过时或JDK不推荐的方法
public class TestStop implements Runnable {
//1.设置标志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while(flag){
System.out.println("run...Thread"+i++);
}
}
//2.设置一个公开的方法停止线程,转换标志位
public void stop() {
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
Thread thread = new Thread(testStop);
thread.start();
for(int i=0;i<1000;i++){
System.out.println("main..."+i);
if(i==900){
//调用stop方法切换标志位,停止线程,
testStop.stop();
System.out.println("stop thread");
}
}
}
}
线程休眠

package state;
import java.util.Date;
//网络延时能避免操作同一对象时
//模拟倒计时
public class TestSleep {
public static void main(String[] args) throws InterruptedException {
/**try {
tenDown();
}catch (InterruptedException e){
e.printStackTrace();
}**/
//打印当前时间
Date startTime = new Date(System.currentTimeMillis());//获取当前时间
while(true){
try {
Thread.sleep(1000);
System.out.println(new Date(System.currentTimeMillis()) + " 倒计时中...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//模拟倒计时
public static void tenDown() throws InterruptedException {
int num = 10;
while(true){
Thread.sleep(1000);
System.out.println(num--);
if(num == 0){
break;
}
}
}
}
线程礼让
package state;
//测试礼让,礼让不一定成功,看CPU心情
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield, "线程1").start();
new Thread(myYield, "线程2").start();
}
}
class MyYield implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName() + "线程停止执行");
}
}
//线程2线程开始执行
//线程1线程开始执行
//线程2线程停止执行
//线程1线程停止执行
Join
package state;
//想像插队
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(" vip来了 " + i);
}
}
public static void main(String[] args) throws InterruptedException {
//启动线程
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
//主线程
for (int i = 0; i < 1000; i++) {
if (i == 200) {
thread.join(); //插队
}
System.out.println("main线程 " + i);
}
}
}
观测线程状态

package state;
//观察线程的状态
public class TestState {
public static void main(String[] args) {
Thread thread =new Thread(()->{
for(int i=0;i<10;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("/////");
});
//观察线程的状态
Thread.State state = thread.getState();
System.out.println(state);
//观察启动
thread.start();
state = thread.getState();
System.out.println(state);//Run
while (state != Thread.State.TERMINATED) {//只要线程没有终止,就一直观察状态
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
state = thread.getState();//更新线程状态
System.out.println(state);//输出线程状态
}
}
}
线程优先级

package state;
//测试线程优先级
public class TestPriority {
public static void main(String[] args) {
//主线程的优先级
System.out.println(Thread.currentThread().getName() + " -->"+Thread.currentThread().getPriority());
//创建线程
Thread t1 = new Thread(new MyPriority(), "t1");
Thread t2 = new Thread(new MyPriority(), "t2");
Thread t3 = new Thread(new MyPriority(), "t3");
Thread t4 = new Thread(new MyPriority(), "t2");
//设置优先级,再启动线程
t1.start();
t2.setPriority(8);
t2.start();
t3.setPriority(Thread.MAX_PRIORITY);
t3.start();
t4.setPriority(Thread.MIN_PRIORITY);
t4.start();
}
}
class MyPriority implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " -->" + Thread.currentThread().getPriority());
}
}
守护线程

package state;
//测试守护进程
//上帝守护你
//守护线程会在用户线程结束后,自动结束,不用手动关闭,会晚一些结束,适用于长时间运行的后台任务
//用户线程线束,虚拟机也会退出
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread t1 = new Thread(god);
Thread t2 = new Thread(you);
t1.setDaemon(true);//默认是false表示是用户线程,正常线程都是用户线程
t1.start();//启动上帝线程
t2.start();//启动你线程
}
}
//上帝
class God implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
while (true) {
System.out.println("上帝守护你");
}
}
}
}
//你
class You implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("你一生都开心的活着");
}
System.out.println("====goodbye world======");
}
}
线程同步(重点)
多个线程操作同一个资源
并发:同一个对象被多个线程同时操作,抢车票,两个银行同时取钱
队列+锁才能保证安全
保证安全,就一定会损失性能,反之也是
三个不安全的案例
package syn;
//不安全的买票
//线程不安全,可能会有负数
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket station = new BuyTicket();
new Thread(station,"me").start();
new Thread(station,"you").start();
new Thread(station,"黄牛").start();
}
}
class BuyTicket implements Runnable {
//票
private int ticket=10;
boolean flag=true;
@Override
public void run() {
//买票
while (flag) {
try{
buy();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
private void buy() throws InterruptedException{
//判断票是否充足
if(ticket<=0){
flag=false;
return;
}
//模拟延迟
Thread.sleep(100);
//买票
System.out.println(Thread.currentThread().getName()+"拿到"+ticket--);
}
}
package syn;
//不安全的取钱
//两个人去银行取钱
//sleep可以放大问题的发生性
public class UnsafeBank {
public static void main(String[] args) {
//账户
Account account = new Account(1000, "结婚基金");
Drawing you = new Drawing(account, 500, "你");
Drawing girlFriend = new Drawing(account, 600, "girlFriend");
you.start();
girlFriend.start();
}
}
//账户
class Account {
int money;
String name;
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
//银行
class Drawing extends Thread {
Account account;
//取了多少钱
int drawMoney;
//现在有多少钱
int nowMoney;
public Drawing(Account account, int drawMoney, String name) {
super(name);
this.account = account;
this.drawMoney = drawMoney;
}
//取钱
@Override
public void run() {
//先判断余额是否足够
if (account.money -drawMoney<0) {
System.out.println(Thread.currentThread().getName() + "取钱失败,余额不足!");
return;
}
//sleep可以放大问题的发生性
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡内余额=yuan卡内余额-取钱数
account.money -= drawMoney;
//你手里的钱
nowMoney += drawMoney;
System.out.println(account.name + "取钱成功,余额为:" + account.money );
System.out.println(Thread.currentThread().getName() + "手里有:" + nowMoney);
//余额足够,开始取钱
}
}
package syn;
import java.util.ArrayList;
//线程不安全的集合
public class UnsafeList {
public static void main(String[] args) {
ArrayList<String> list= new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}//9998,还是有可能出现线程安全问题的。
同步方法和同步块
同一个对象被两个线程中启用对象的synchronized方法时,会依次排队执行,而不会共同执行,引起数据错乱

package syn;
//不安全的买票
//线程不安全,可能会有负数
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket station = new BuyTicket();
new Thread(station,"me").start();//操作同一个对象
new Thread(station,"you").start();
new Thread(station,"黄牛").start();
}
}
class BuyTicket implements Runnable {
//票
private int ticket=10;
boolean flag=true;
@Override
public void run() {
//买票
while (flag) {
try{
buy();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
//synchronized关键字保证线程安全,同步方法,锁的是this
//锁的是this,所以多个线程操作的是同一个对象,互斥
private synchronized void buy() throws InterruptedException{
//判断票是否充足
if(ticket<=0){
flag=false;
return;
}
//模拟延迟
//Thread.sleep(100);
//买票
System.out.println(Thread.currentThread().getName()+"拿到"+ticket--);
}
}
方法里面需要修改的内容才需要锁,锁的太多,浪费资源

package syn;
//不安全的取钱
//两个人去银行取钱
//sleep可以放大问题的发生性
public class UnsafeBank {
public static void main(String[] args) {
//账户
Account account = new Account(1000, "结婚基金");
Drawing you = new Drawing(account, 500, "你");
Drawing girlFriend = new Drawing(account, 600, "girlFriend");
you.start();
girlFriend.start();
}
}
//账户
class Account {
int money;
String name;
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
//银行
class Drawing extends Thread {
Account account;
//取了多少钱
int drawMoney;
//现在有多少钱
int nowMoney;
public Drawing(Account account, int drawMoney, String name) {
super(name);
this.account = account;
this.drawMoney = drawMoney;
}
//取钱
@Override
public void run() {
//锁的对象是变化的量,需要增删改
synchronized (account) {
//先判断余额是否足够
if (account.money - drawMoney < 0) {
System.out.println(Thread.currentThread().getName() + "取钱失败,余额不足!");
return;
}
//sleep可以放大问题的发生性
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡内余额=yuan卡内余额-取钱数
account.money -= drawMoney;
//你手里的钱
nowMoney += drawMoney;
System.out.println(account.name + "取钱成功,余额为:" + account.money);
System.out.println(Thread.currentThread().getName() + "手里有:" + nowMoney);
//余额足够,开始取钱
}
}
}
package syn;
import java.util.ArrayList;
//线程不安全的集合
public class UnsafeList {
public static void main(String[] args) {
ArrayList<String> list= new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}//9998,还是有可能出现线程安全问题的。
package syn;
import java.util.concurrent.CopyOnWriteArrayList;
//测试JUC安全类型的集合,安全的类
public class TestJUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
同步方法和同步方法块的区别
package FF;
public class SynchronizationExample {
public static void main(String[] args) {
// 创建一个银行账户,初始余额为 1000 元
BankAccount account = new BankAccount(1000);
// 创建两个线程,使用同步方法进行存款
Thread thread1 = new DepositThread(account, 500, true);
Thread thread2 = new DepositThread(account, 300, true);
// 启动线程
thread1.start();
thread2.start();
try {
// 等待线程执行完毕
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("使用同步方法存款后,账户余额: " + account.getBalance() + " 元");
// 创建另外两个线程,使用同步块进行存款
Thread thread3 = new DepositThread(account, 200, false);
Thread thread4 = new DepositThread(account, 400, false);
// 启动线程
thread3.start();
thread4.start();
try {
// 等待线程执行完毕
thread3.join();
thread4.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("使用同步块存款后,账户余额: " + account.getBalance() + " 元");
}
}
// 银行账户类
class BankAccount {
private double balance;
public BankAccount(double initialBalance) {
this.balance = initialBalance;
}
// 同步方法:存款操作
public synchronized void depositUsingSyncMethod(double amount) {
System.out.println(Thread.currentThread().getName() + " 进入同步方法开始存款...");
double temp = balance;
try {
// 模拟一些耗时操作
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
temp += amount;
balance = temp;
System.out.println(Thread.currentThread().getName() + " 存款 " + amount + " 元,当前余额: " + balance + " 元");
}
// 非同步方法,使用同步块进行存款操作
public void depositUsingSyncBlock(double amount) {
System.out.println(Thread.currentThread().getName() + " 进入方法开始存款...");
// 同步块,使用当前对象作为锁
synchronized (this) {
System.out.println(Thread.currentThread().getName() + " 进入同步块开始存款...");
double temp = balance;
try {
// 模拟一些耗时操作
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
temp += amount;
balance = temp;
System.out.println(Thread.currentThread().getName() + " 存款 " + amount + " 元,当前余额: " + balance + " 元");
}
System.out.println(Thread.currentThread().getName() + " 离开方法");
}
public double getBalance() {
return balance;
}
}
// 存款线程类
class DepositThread extends Thread {
private BankAccount account;
private double amount;
private boolean useSyncMethod;
public DepositThread(BankAccount account, double amount, boolean useSyncMethod) {
this.account = account;
this.amount = amount;
this.useSyncMethod = useSyncMethod;
}
@Override
public void run() {
if (useSyncMethod) {
account.depositUsingSyncMethod(amount);
} else {
account.depositUsingSyncBlock(amount);
}
}
}
//Thread-0 进入同步方法开始存款...
//Thread-0 存款 500.0 元,当前余额: 1500.0 元
//Thread-1 进入同步方法开始存款...
//Thread-1 存款 300.0 元,当前余额: 1800.0 元
//使用同步方法存款后,账户余额: 1800.0 元
//Thread-2 进入方法开始存款...
//Thread-3 进入方法开始存款...
//Thread-2 进入同步块开始存款...
//Thread-2 存款 200.0 元,当前余额: 2000.0 元
//Thread-3 进入同步块开始存款...
//Thread-2 离开方法
//Thread-3 存款 400.0 元,当前余额: 2400.0 元
//Thread-3 离开方法
//使用同步块存款后,账户余额: 2400.0 元
1、同步方法 - 非静态同步方法以this为锁
非静态同步方法的锁是当前对象this。这意味着当一个线程进入非静态同步方法时,它会获取当前对象的锁,其他线程如果想要进入同一个对象的任何非静态同步方法,就必须等待这个锁的释放。
public class NonStaticSyncMethodExample {
private int data = 0;
// 非静态同步方法
public synchronized void increment() {
data++;
System.out.println(Thread.currentThread().getName() + " 执行 increment 方法,data = " + data);
}
public static void main(String[] args) {
NonStaticSyncMethodExample example = new NonStaticSyncMethodExample();
//同一个对象example,建立了两个线程,两个线程中的方法都是用了上面的increment,可以理解为执行了两遍方法
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
example.increment();
}
}, "线程1");
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
example.increment();
}
}, "线程2");
thread1.start();
thread2.start();
}
}
在这个例子中,increment方法是一个非静态同步方法。两个线程线程1和线程2都在操作同一个NonStaticSyncMethodExample对象的increment方法。由于非静态同步方法的锁是this(即当前对象),所以同一时间只有一个线程可以进入increment方法,保证了data自增操作的线程安全性。
2、同步方法 - 静态同步方法以类的Class对象为锁
静态同步方法的锁是当前类的Class对象。这意味着对于所有实例,只要是调用静态同步方法,都会竞争同一个类的Class对象锁。
public class StaticSyncMethodExample {
private static int sharedData = 0;
// 静态同步方法
public static synchronized void increment() {
sharedData++;
System.out.println(Thread.currentThread().getName() + " 执行 increment 方法,sharedData = " + sharedData);
}
public static void main(String[] args) {
StaticSyncMethodExample example1 = new StaticSyncMethodExample();
StaticSyncMethodExample example2 = new StaticSyncMethodExample();
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
example1.increment();
}
}, "线程1");
Thread thread2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
example2.increment();
}
}, "线程2");
thread1.start();
thread2.start();
}
}
在这个例子中,increment是静态同步方法。即使有两个不同的StaticSyncMethodExample对象(example1和example2),但由于静态同步方法的锁是类的Class对象,所以线程1和线程2在调用increment方法时,依然会竞争同一个锁,保证了sharedData自增操作的线程安全性。
3、同步方法块 - 可以指定任意对象作为锁
同步方法块可以指定任意对象作为锁,这使得同步的控制更加灵活。可以根据具体需求,选择不同的锁对象来控制不同代码块的同步。
同步方法块的几种方法
以实例对象为锁
public class InstanceLockSyncBlock {
private int data = 0;
public void increment() {
// 使用this作为锁,保护对data的操作
synchronized (this) {
data++;
}
}
}
以类对象为锁
public class ClassLockSyncBlock {
private static int data = 0;
public static void increment() {
// 使用类对象作为锁,保护对静态变量data的操作
synchronized (ClassLockSyncBlock.class) {
data++;
}
}
}
以自定义对象为锁
public class CustomLockSyncBlock {
private int data = 0;
private final Object lock = new Object();
public void increment() {
// 使用自定义的lock对象作为锁
synchronized (lock) {
data++;
}
}
}
死锁


死锁案例
public class DeadlockExample {
// 创建两个对象作为锁
private static final Object resource1 = new Object();
private static final Object resource2 = new Object();
public static void main(String[] args) {
// 创建第一个线程
Thread thread1 = new Thread(() -> {
synchronized (resource1) {
System.out.println("Thread 1: Holding resource 1...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Waiting for resource 2...");
synchronized (resource2) {
System.out.println("Thread 1: Holding resource 1 and resource 2...");
}
}
});
// 创建第二个线程
Thread thread2 = new Thread(() -> {
synchronized (resource2) {
System.out.println("Thread 2: Holding resource 2...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: Waiting for resource 1...");
synchronized (resource1) {
System.out.println("Thread 2: Holding resource 2 and resource 1...");
}
}
});
// 启动两个线程
thread1.start();
thread2.start();
}
}
代码解释
资源定义:创建了两个对象 resource1 和 resource2 作为需要争夺的资源。
线程创建:
thread1 首先获取 resource1 的锁,然后休眠 100 毫秒,之后尝试获取 resource2 的锁。
thread2 首先获取 resource2 的锁,然后休眠 100 毫秒,之后尝试获取 resource1 的锁。
死锁发生:当 thread1 持有 resource1 并等待 resource2 时,thread2 持有 resource2 并等待 resource1,此时两个线程都在等待对方释放资源,从而形成了死锁。
如何解锁
public class DeadlockAvoidanceExample {
// 创建两个对象作为锁
private static final Object resource1 = new Object();
private static final Object resource2 = new Object();
public static void main(String[] args) {
// 创建第一个线程
Thread thread1 = new Thread(() -> {
// 按照相同顺序获取锁,先获取 resource1,再获取 resource2
synchronized (resource1) {
System.out.println("Thread 1: Holding resource 1...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 1: Waiting for resource 2...");
synchronized (resource2) {
System.out.println("Thread 1: Holding resource 1 and resource 2...");
}
}
});
// 创建第二个线程
Thread thread2 = new Thread(() -> {
// 同样按照相同顺序获取锁,先获取 resource1,再获取 resource2
synchronized (resource1) {
System.out.println("Thread 2: Holding resource 1...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: Waiting for resource 2...");
synchronized (resource2) {
System.out.println("Thread 2: Holding resource 1 and resource 2...");
}
}
});
// 启动两个线程
thread1.start();
thread2.start();
}
}
代码解释
获取锁的顺序:在原代码中,thread1 先获取 resource1 再尝试获取 resource2,而 thread2 先获取 resource2 再尝试获取 resource1,这就可能导致环路等待。修改后的代码让两个线程都按照先获取 resource1 再获取 resource2 的顺序来获取锁。
避免死锁原理:当 thread1 先获取了 resource1 后,thread2 会等待 resource1 的释放,只有当 thread1 释放了 resource1 并且 thread2 获取到 resource1 之后,thread2 才会去尝试获取 resource2,这样就避免了两个线程相互等待对方持有的锁,从而破坏了环路等待条件,避免了死锁的发生。
除了统一锁的获取顺序,还可以采用以下方法避免死锁:
Lock锁



package syn;
import java.util.concurrent.locks.ReentrantLock;
//测试锁
public class TestLock {
public static void main(String[] args) {
TestLock2 t = new TestLock2();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.start();
t2.start();
t3.start();
}
}
class TestLock2 implements Runnable {
int ticket = 10;
//定义lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();//加锁
if (ticket > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(ticket--);
} else {
break;
}
}finally{
lock.unlock();
}
//解锁
}
}
}
线程通信问题
线程协作(生产者和消费者模式)



管程法
package syn;
//测试生产者消费者模型--》利用缓冲区解决:管程法
public class TestPc {
public static void main(String[] args) {
SynContainer container = new SynContainer();
Producer producer = new Producer(container);
Consumer consumer = new Consumer(container);
producer.start();
consumer.start();
}
}
//生产者
class Producer extends Thread {
SynContainer container;
public Producer(SynContainer container) {
this.container = container;
}
//生产
@Override
public void run() {
for (int i = 0; i < 100 ;i++) {
container.push(new Chicken(i));
System.out.println("生产了" + i+"只鸡");
}
}
}
//消费者
class Consumer extends Thread {
SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
//消费
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了" + container.pop().i + "只鸡");
}
}
}
//产品
class Chicken{
int i;
public Chicken(int i) {
this.i = i;
}
}
//缓冲区
class SynContainer {
Chicken[] chickens = new Chicken[10];
//容器计数器
int count = 0;
//生产者放入产品
public synchronized void push(Chicken chicken) {
//如果容器满了,就需要等待消费者
if (count == chickens.length) {
//通知消费者可以取出产品,生产等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果容器没有满,就直接放入产品
chickens[count] = chicken;
count++;
//可以通知消费者可以取出产品
this.notify();
}
//消费者取出产品
public synchronized Chicken pop() {
//判断能否消费
if (count ==0) {
//等待生产者生产,消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消费
count--;
Chicken chicken = chickens[count];
//吃完了产品,通知生产者可以生产
return chicken;
}
}
第二个案例
// 缓冲区类,用于存储生产者生产的数据
class Buffer {
private static final int MAX_SIZE = 5;
private int[] buffer;
private int count;
private int in;
private int out;
public Buffer() {
buffer = new int[MAX_SIZE];
count = 0;
in = 0;
out = 0;
}
// 生产者调用的方法,用于向缓冲区中添加数据
public synchronized void produce(int item) throws InterruptedException {
// 当缓冲区已满时,生产者线程等待
while (count == MAX_SIZE) {
wait();
}
// 将数据添加到缓冲区
buffer[in] = item;
in = (in + 1) % MAX_SIZE;
count++;
System.out.println("生产者生产了: " + item);
// 通知可能正在等待的消费者线程
notifyAll();
}
// 消费者调用的方法,用于从缓冲区中取出数据
public synchronized int consume() throws InterruptedException {
// 当缓冲区为空时,消费者线程等待
while (count == 0) {
wait();
}
// 从缓冲区中取出数据
int item = buffer[out];
out = (out + 1) % MAX_SIZE;
count--;
System.out.println("消费者消费了: " + item);
// 通知可能正在等待的生产者线程
notifyAll();
return item;
}
}
// 生产者线程类
class Producer implements Runnable {
private Buffer buffer;
public Producer(Buffer buffer) {
this.buffer = buffer;
}
@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
buffer.produce(i);
Thread.sleep(100);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
// 消费者线程类
class Consumer implements Runnable {
private Buffer buffer;
public Consumer(Buffer buffer) {
this.buffer = buffer;
}
@Override
public void run() {
try {
for (int i = 0; i < 10; i++) {
buffer.consume();
Thread.sleep(200);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
// 主类,用于启动生产者和消费者线程
public class MonitorMethodExample {
public static void main(String[] args) {
Buffer buffer = new Buffer();
Thread producerThread = new Thread(new Producer(buffer));
Thread consumerThread = new Thread(new Consumer(buffer));
producerThread.start();
consumerThread.start();
try {
producerThread.join();
consumerThread.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
信号灯法
// 共享资源类,包含信号灯和数据
class SharedData {
// 存储生产的数据
private String data;
// 信号灯,true 表示数据已生产,可以消费;false 表示需要生产数据
private boolean isProduced = false;
// 生产者调用此方法生产数据
public synchronized void produce(String newData) {
// 当数据已生产且未被消费时,生产者线程等待
while (isProduced) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 生产数据
this.data = newData;
System.out.println("生产者生产了: " + data);
// 标记数据已生产
isProduced = true;
// 唤醒可能正在等待的消费者线程
notifyAll();
}
// 消费者调用此方法消费数据
public synchronized String consume() {
// 当数据未生产时,消费者线程等待
while (!isProduced) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 消费数据
System.out.println("消费者消费了: " + data);
// 标记数据已消费
isProduced = false;
// 唤醒可能正在等待的生产者线程
notifyAll();
return data;
}
}
// 生产者线程类
class ProducerThread implements Runnable {
private SharedData sharedData;
public ProducerThread(SharedData sharedData) {
this.sharedData = sharedData;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
// 模拟生产的数据
String product = "产品" + i;
sharedData.produce(product);
try {
// 模拟生产所需时间
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
// 消费者线程类
class ConsumerThread implements Runnable {
private SharedData sharedData;
public ConsumerThread(SharedData sharedData) {
this.sharedData = sharedData;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
sharedData.consume();
try {
// 模拟消费所需时间
Thread.sleep(800);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
// 主类,程序入口
public class SignalLightExample {
public static void main(String[] args) {
// 创建共享资源对象
SharedData sharedData = new SharedData();
// 创建生产者线程
Thread producer = new Thread(new ProducerThread(sharedData));
// 创建消费者线程
Thread consumer = new Thread(new ConsumerThread(sharedData));
// 启动生产者线程
producer.start();
// 启动消费者线程
consumer.start();
try {
// 等待生产者线程执行完毕
producer.join();
// 等待消费者线程执行完毕
consumer.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
使用线程池


package syn;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//测试线程池
public class TestPool {
public static void main(String[] args) {
// 创建服务,创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
// 执行任务
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
// 关闭线程池
service.shutdown();
}
}
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}

浙公网安备 33010602011771号