JUC 练习代码,让你深入立即 JUC

例1:

public class MyServlet extends HttpServlet {
// 是否安全
Map<String, Object> map = new HashMap<>();
// 是否安全
String S1 = "...";
// 是否安全
final String s2 = "...";
Date D1 = new Date();
// 是否安全
final Date D2 = new Date();

public void doGet(HttpServletRequest request, HttpServletResponse response) {
		// 使用上述变量
	}
}
分析

image-20250319215419351

答案

Map<String, Object> map = new ConcurrentHashMap<>();
final LocalDateTime s2 = LocalDateTime.now();
LocalDateTime D1 = LocalDateTime.now();
final LocalDateTime D2 = LocalDateTime.now();

例二

public class MyServlet extends HttpServlet {
	// 是否安全?
	private UserService userService = new UserServiceImpl();
	
	public void doGet(HttpServletRequest request, HttpServletResponse response) {
		userService.update(...);
	}
}
public class UserServiceImpl implements UserService {
	// 记录调用次数
	private int count = 0;
	
	public void update() {
		// ...
		count++;
	}
}
思路

image-20250319215650756

答案

public class MyServlet extends HttpServlet {
    private UserService userService = new UserServiceImpl();
    
    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        userService.update();
    }
}

public class UserServiceImpl implements UserService {
    private AtomicInteger count = new AtomicInteger(0);
    
    public void update() {
        // ...
        count.incrementAndGet();
    }
}

例三

@Aspect
@Component
public class MyAspect// 是否安全
	private long start = 0L;
	
	@Before("execution(* *(..))")
	 public void before() {
	 	start = System.nanoTime();
	 }
	@After("execution(* *(..))")
	public void after() {
		long end = System.nanoTime();
		System.out.println("cost time:" + (end - start));
	}
}
思路
思路一
@Aspect
@Componet
public class MyAspect {
    private ThreadLocal<Long> startTime = new ThreadLocal<>();
    
    @Before("execution(* *(..))")
    public void before() {
        startTime.set(System.nanoTime());
    }
    
    @After("execution(* *(..))")
    public void after() {
        long end = System.nanoTime();
        long start = startTime.get();
        System.out.println("cost time:" + (end - start));
        startTime.remove(); // 清理 ThreadLocal, 防止内存泄漏
    }
}

对象

  • 线程安全,每个线程都有自己的 start
  • 简单易用,适用这种需要在线程间隔离状态的场景

缺点

  • 需要手动清理 ThreadLocal (调用 remove()), 否则可能会导致内存泄漏(尤其是在线程池场景中)
思路二
@Aspect
@Component
public class MyAspect {
    @Around("execution(* *(..))")
    public void around(ProceddingJoinPoint joinPoint) throws Thtowable {
        long start = System.nanoTime();
        joinPoint.proceed() // 执行目标方法
        long end = System.nanoTime();
        System.out.println("cost time:" + (end - start));
    }
}

答案

@Aspect
@Component
public class MyAspect {
    private ThreadLoca<Long> startTime = new ThreaddLocal<>();
    
    @Before("execution(* *(..))")
    public void before() {
        startTime.set(System.nanoTime());
    }
    
    @After("execution( *(..))")
    public void after() {
        long end = System.nanoTime();
        long start = System.get();
        System.out.println("cost time:" + (end - start));
        startTime.remove(); // 清理 ThreadLocal, 防止内存泄漏
    }
}

例四

public class MyServlet extends HttpServlet {
	// 是否安全
	private UserService userService = new UserServiceImpl();
	
	public void doGet(HttpServletRequest request, HttpServletResponse response) {
		userService.update(...);
	}
}

public class UserServiceImpl implements UserService {
	// 是否安全
	private UserDao userDao = new UserDaoImpl();
	
	public void update() {
		userDao.update();
	}
}

public class UserDaoImpl implements UserDao {
	public void update() {
		String sql = "update user set password = ? where username = ?"
		// 是否安全
		try (Connection conn = DriverManager.getConnection("","","")) {
			// ...
		} catch (Exception e) {
			// ...
		}
	}
}
思路

image-20250319221845169

image-20250319221857971

image-20250319221913120

答案

public class MyServlet extends HttpServlet {
    private UserService userService = new UserServiceImpl();

    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        userService.update(...);
    }
}

public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoImpl();

    public void update() {
        userDao.update();
    }
}

public class UserDaoImpl implements UserDao {
    private DataSource dataSource; // 通过依赖注入获取连接池

    public UserDaoImpl(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void update() {
        String sql = "update user set password = ? where username = ?";
        try (Connection conn = dataSource.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            pstmt.setString(1, password);
            pstmt.setString(2, username);
            pstmt.executeUpdate();
        } catch (Exception e) {
            // ...
        }
    }
}

例5

public class MyServlet extends HttpServlet {
	// 是否安全
	private UserService userService = new UserServiceImpl();
	
	public void doGet(HttpServletRequest request, HttpServletResponse response) 【
		userService.update(...);
	}
}

public class UserServiceImpl implements UserService {
	// 是否安全
	private UserDao userDao = new UserDaoImpl();
	
	public void update() {
		userDao.update();
	}
}

public class UserDaoImp implements UserDao {
	// 是否安全
	private Connection conn = null;
	
	public void update() throws SQLException {
		String sql = "update user set password = ? where username = ?"
		conn = DriverManager.getConnection("","","");
		// ...
		conn.close();
	}
}
思路

image-20250319223912910

答案

public class MyServlet extends HttpServlet {
    private UserService userService = new UserServiceImpl();

    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        userService.update(...);
    }
}

public class UserServiceImpl implements UserService {
    private UserDao userDao = new UserDaoImpl();

    public void update() {
        userDao.update();
    }
}

public class UserDaoImpl implements UserDao {
    private DataSource dataSource; // 通过依赖注入获取连接池

    public UserDaoImpl(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void update() throws SQLException {
        String sql = "update user set password = ? where username = ?";
        try (Connection conn = dataSource.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            // 设置参数
            pstmt.setString(1, password);
            pstmt.setString(2, username);
            pstmt.executeUpdate();
        }
    }
}

例6

public class MyServlet extedns HttpServlet {
	// 是否安全
	private UserService userService = new UserServiceImpl();
	
	public void doGet(HttpServletRequest request, HttpServletReponse response) {
			userService.update(...);
		}
}

public class UserServiceImpl implements UserService {
	pubilc void update() {
		UserDao userDao = new UserDaoImpl();
		userDao.update();
	}
}

public class UserDaoImpl implements UserDao  {
	// 是否安全
	private Connection = null;
	public void update() throws SQLException {
		String sql = "update user set password = ? where username  ?"
		conn = DriverManager.getConnection("", "", "")l
		// ...
		conn.close();
	}
}	
思路

image-20250319224224282

答案

public class UserDaoImpl implements UserDao {
    private DataSource dataSource; // 通过依赖注入获取连接池

    public UserDaoImpl(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void update() throws SQLException {
        String sql = "update user set password = ? where username = ?";
        try (Connection conn = dataSource.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            // 设置参数
            pstmt.setString(1, password);
            pstmt.setString(2, username);
            pstmt.executeUpdate();
        }
    }
}

例7

public abstract class Test {
	
	public void bar() {
		// 是否安全
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		foo(sdf);
	}
	
	public abstract foo(SimpleDateFormat sdf);
	
	public static void main(String[] args) {
		new Test().bar();
	}
}

其中 foo 的行为是不确定的,可能导致不安全的发生,被称为外星方法

public void foo(SimpleDateFormat sdf) {
	String dateStr = "1999-10-11 00:00:00";
	for (int i = 0; i < 20; i++) {
		new Thread(() -> {
			try {
				sdf.parse(dateStr);
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}).start();
	}
}

请比较 JDK 中 String 类的实现

思路

image-20250319224336524

答案

public void foo() {
    String dateStr = "1999-10-11 00:00:00";
    for (int i = 0; i < 20; i++) {
        new Thread(() -> {
            try {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                sdf.parse(dateStr); // 每个线程有自己的 sdf 实例
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

例八

private static Integer i = 0;
public static void main(String[] args) throws InterruptedExceptionList<Thread> list = new ArrayLIst<>();
	for (int j = 0; j < 2; j++) {
		Thread thread = new Thread(() -> {
			for (int k = 0; k < 5000; k++) [
				synchronized (i) {
					i++;
				}
			}
		}, " " + j);
		list.add(thread);
	}
	list.stream().forEach(t -> t.start());
	list.stream().forEach(t  -> {
		try {
			t.join();
		}  catch (InterruptedException e) {
			e.printStackTrace();
		}
	});
思路

image-20250319224455688

  1. 适用 AtomicInteger
  2. 适用显式锁(ReentrantLock)
  3. 使用 synchronized 锁固定对象

答案

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    private static AtomicInteger i = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        List<Thread> list = new ArrayList<>();
        for (int j = 0; j < 2; j++) {
            Thread thread = new Thread(() -> {
                for (int k = 0; k < 5000; k++) {
                    i.incrementAndGet(); // 原子操作
                }
            }, " " + j);
            list.add(thread);
        }
        list.forEach(Thread::start);
        list.forEach(t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println("Final value of i: " + i.get());
    }
}
卖票问题

image-20250319224946924

image-20250319225131382

public class ExerciseSell {
    public static void main(String[] args) {
        TicketWindow ticketWindow = new TicketWindow(2000);
        List<Thread> list = new ArrayList<>();
        // 用来存储买出去多少张票
        List<Integer> sellCount = new Vector<>();
        for (int i = 0; i < 2000; i++) {
            Thread t = new Thread(() -> {
                // 分析这里的竞态条件
                int count = ticketWindow.sell(randomAmount());
                sellCount.add(count);
            });
            list.add(t);
            t.start();
        }
        list.forEach((t) -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        // 买出去的票求和
        log.debug("selled count:{}", sellCount.stream().mapToInt(c -> c).sum());
        // 剩余票数
        log.debug("remainder count:{}", ticketWindow.getCount());
    }

    // Random 为线程安全
    static Random random = new Random();

    // 随机 1~5
    public static int randomAmount() {
        return random.nextInt(5) + 1;
    }
}

class TicketWindow {
    private int count;

    public TicketWindow(int count) {
        this.count = count;
    }

    public int getCount() {
        return count;
    }

    public int sell(int amount) {
        if (this.count >= amount) {
            this.count -= amount;
            return amount;
        } else {
            return 0;
        }
    }
}

image-20250319225110511

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Vector;

public class ExerciseSell {
    public static void main(String[] args) {
        TicketWindow ticketWindow = new TicketWindow(2000);
        List<Thread> list = new ArrayList<>();
        List<Integer> sellCount = new Vector<>(); // 使用 Vector 确保线程安全

        for (int i = 0; i < 2000; i++) {
            Thread t = new Thread(() -> {
                int count = ticketWindow.sell(randomAmount());
                sellCount.add(count);
            });
            list.add(t);
            t.start();
        }

        list.forEach((t) -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        System.out.println("selled count: " + sellCount.stream().mapToInt(c -> c).sum());
        System.out.println("remainder count: " + ticketWindow.getCount());
    }

    static Random random = new Random();

    public static int randomAmount() {
        return random.nextInt(5) + 1;
    }
}

class TicketWindow {
    private int count;

    public TicketWindow(int count) {
        this.count = count;
    }

    public synchronized int getCount() {
        return count;
    }

    public synchronized int sell(int amount) {
        if (this.count >= amount) {
            this.count -= amount;
            return amount;
        } else {
            return 0;
        }
    }
}
转账练习
public class ExerciseTransfer {
    public static void main(String[] args) throws InterruptedException {
        Account a = new Account(1000);
        Account b = new Account(1000);
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                a.transfer(b, randomAmount());
            }
        }, "t1");
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                b.transfer(a, randomAmount());
            }
        }, "t2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        // 查看转账2000次后的总金额
        log.debug("total:{}", (a.getMoney() + b.getMoney()));
    }

    // Random 为线程安全
    static Random random = new Random();

    // 随机 1~100
    public static int randomAmount() {
        return random.nextInt(100) + 1;
    }
}

class Account {
    private int money;

    public Account(int money) {
        this.money = money;
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public void transfer(Account target, int amount) {
        if (this.money > amount) {
            this.setMoney(this.getMoney() - amount);
            target.setMoney(target.getMoney() + amount);
        }
    }

    }
    这样改正行不行,为什么?

    public synchronized void transfer(Account target, int amount) {
    if (this.money > amount) {
    this.setMoney(this.getMoney() - amount);
    target.setMoney(target.getMoney() + amount);
    }
    }

image-20250319231751711

改进后的代码使用全局锁
import java.util.Random;

public class ExerciseTransfer {
    public static void main(String[] args) throws InterruptedException {
        Account a = new Account(1000);
        Account b = new Account(1000);
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                a.transfer(b, randomAmount());
            }
        }, "t1");
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                b.transfer(a, randomAmount());
            }
        }, "t2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("total: " + (a.getMoney() + b.getMoney()));
    }

    static Random random = new Random();

    public static int randomAmount() {
        return random.nextInt(100) + 1;
    }
}

class Account {
    private int money;

    public Account(int money) {
        this.money = money;
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public void transfer(Account target, int amount) {
        synchronized (Account.class) {
            if (this.money > amount) {
                this.setMoney(this.getMoney() - amount);
                target.setMoney(target.getMoney() + amount);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值