同步锁

同步与异步

同步:体现了排队的效果,同一时刻只能有一个线程独占资源,其他没有权利的线程排队。
坏处就是效率会降低,不过保证了安全。
异步:体现了多线程抢占资源的效果,线程间互相不等待,互相抢占资源。
坏处就是有安全隐患,效率要高一些。

synchronized关键字

语法格式:

  1. synchronized (锁对象){
  2. 需要同步的代码(也就是可能出现问题的操作共享数据的多条语句);
  3. }

前提

  • 同步需要两个或者两个以上的线程(单线程无需考虑多线程安全问题)
  • 多个线程间必须使用同一个锁

特点

synchronized同步关键字可以用来修饰方法,称为同步方法,使用的锁对象是this
synchronized同步关键字可以用来修饰代码块,称为同步代码块,使用的锁对象可以任意
同步的缺点是会降低程序的执行效率,但我们为了保证线程的安全,有些性能是必须要牺牲的
为了性能,加锁的范围需要控制好

售票案例

Runnable

  1. package cn.tedu.tickets;
  2. /**
  3. * 解决接口方式售票安全隐患问题
  4. */
  5. public class TicketRunnableV2 {
  6. public static void main(String[] args) {
  7. TicketRV2 t = new TicketRV2();//统一的目标业务对象
  8. Thread m1 = new Thread(t,"马钊");//多线程对象
  9. Thread m2 = new Thread(t,"雨来");//多线程对象
  10. Thread m3 = new Thread(t,"泡泡");//多线程对象
  11. Thread m4 = new Thread(t,"赵彪");//多线程对象
  12. m1.start();
  13. m2.start();
  14. m3.start();
  15. m4.start();
  16. }
  17. }
  18. class TicketRV2 implements Runnable{
  19. int ticket = 100;
  20. //定义一个唯一的锁对象
  21. Object o =new Object();
  22. @Override
  23. public void run() {
  24. while (true) {
  25. /*
  26. synchronized (new Object())
  27. 锁不住,会new多个Object()对象
  28. 需要定义一个同步代码块,锁对象需要唯一
  29. */
  30. synchronized (o) {
  31. if (ticket > 0){
  32. try {
  33. Thread.sleep(10);
  34. } catch (InterruptedException e) {
  35. e.printStackTrace();
  36. }
  37. System.out.println(Thread.currentThread().getName() + ":" + ticket--);
  38. } else break;
  39. }
  40. }
  41. }
  42. }

Thread

  1. package cn.tedu.tickets;
  2. /**
  3. * 解决继承Thread方式售票安全隐患问题
  4. */
  5. public class TicketThreadV2 {
  6. public static void main(String[] args) {
  7. TicketTV2 t1 = new TicketTV2("1号窗口马钊");
  8. TicketTV2 t2 = new TicketTV2("2号窗口桂宏宇");
  9. TicketTV2 t3 = new TicketTV2("3号窗口雨来");
  10. TicketTV2 t4 = new TicketTV2("4号窗口泡泡");
  11. t1.start();
  12. t2.start();
  13. t3.start();
  14. t4.start();
  15. }
  16. }
  17. class TicketTV2 extends Thread{
  18. static int ticket = 100;
  19. //Object o = new Object();
  20. public TicketTV2(String name) {
  21. super(name);
  22. }
  23. @Override
  24. public void run() {
  25. while (true){
  26. synchronized (TicketTV2.class){
  27. if (ticket >0){
  28. try {
  29. Thread.sleep(10);
  30. } catch (InterruptedException e) {
  31. e.printStackTrace();
  32. }
  33. System.out.println(getName() + "=" + ticket--);
  34. } else break;
  35. }
  36. }
  37. }
  38. }

StringBuffer与StringBuilder

StringBuffer
加了synchronized ,性能相对较低(要排队,同步),安全性高
StringBuilder
去掉了synchronized,性能更高(不排队,异步),存在安全隐患

线程创建其他方式

ExecutorService/Executors

ExecutorService:用来存储线程的池子,把新建线程/启动线程/关闭线程的任务都交给池来管理

  • execute(Runnable任务对象) 把任务丢到线程池

Executors 辅助创建线程池的工具类

  • newFixedThreadPool(int nThreads) 最多n个线程的线程池
  • newCachedThreadPool() 足够多的线程,使任务不必等待
  • newSingleThreadExecutor() 只有一个线程的线程池

示例

  1. /*
  2. 创建线程池
  3. Executors
  4. newFixedThreadPool(线程数)方法创建指定线程数的线程池
  5. 线程池类型为:ExecutorService
  6. */
  7. ExecutorService pool = Executors.newFixedThreadPool(5);
  8. for (int i = 0;i < 5;i++){
  9. //使用池对象完成任务,人任务参数为target
  10. pool.execute(target);
  11. }