一,问题背景
1.为什么要引入多线程?
用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现。说这个话其实只有一半对,因为反应“多角色”的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的“生产者,消费者模型”。
2.多线程、同步、并发概念:
多线程:指的是这个程序(一个进程)运行时产生了不止一个线程。
并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。
3.引入多线程后会带来那些问题?
java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。
以买票系统为例,我们发现不加控制多线程会出现超卖现象。
1 public class RunnableImpl implements Runnable { 2 3 private int ticket=100; 4 @Override 5 public void run() { 6 while(true){ 7 if (ticket<=0){ 8 break; 9 } 10 try { 11 Thread.sleep(200); 12 String name = Thread.currentThread().getName(); 13 System.out.println("第"+name+"窗口正在卖出第"+(200-ticket+1)+"张票,剩余"+(--ticket)+"张"); 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 18 } 19 20 21 22 } 23 }
测试类:
1 public class originticket { 2 public static void main(String[] args) { 3 RunnableImpl runnable = new RunnableImpl(); 4 new Thread(runnable,"一").start(); 5 new Thread(runnable,"二").start(); 6 } 7 }
结果显示:
二,问题解决方案之synchronize代码块:
1 public class RunnableImplsyn implements Runnable { 2 3 private int ticket=200; 4 @Override 5 public void run() { 6 while(true) { 7 try { 8 Thread.sleep(200); 9 } catch (InterruptedException e) { 10 e.printStackTrace(); 11 } 12 synchronized (this) { 13 if (ticket <= 0) { 14 break; 15 } 16 17 18 String name = Thread.currentThread().getName(); 19 System.out.println("第" + name + "窗口正在卖出第" + (200 - ticket + 1) + "张票,剩余" + (--ticket) + "张"); 20 21 22 } 23 } 24 25 26 } 27 }
测试类:
1 public class synchronizeblockTicket { 2 3 public static void main(String[] args) { 4 RunnableImplsyn runnableImplsyn = new RunnableImplsyn(); 5 new Thread(runnableImplsyn,"一").start(); 6 new Thread(runnableImplsyn,"二").start(); 7 } 8 }
结果:
三,问题解决方案之synchronize方法:
这里抽取了方法,this代指:
1 public class RunnableImplsynfn implements Runnable { 2 3 private static int ticket=200; 4 @Override 5 public void run() { 6 test(); 7 8 } 9 10 11 private synchronized void test() { 12 while(true){ 13 if (ticket<=0){ 14 break; 15 } 16 try { 17 Thread.sleep(200); 18 String name = Thread.currentThread().getName(); 19 System.out.println("第"+name+"窗口正在卖出第"+(200-ticket+1)+"张票,剩余"+(--ticket)+"张"); 20 } catch (InterruptedException e) { 21 e.printStackTrace(); 22 } 23 24 } 25 26 27 28 } 29 }
测试类:
1 public class synchronizefnTicket { 2 3 4 public static void main(String[] args) { 5 RunnableImplsynfn runnableImplsyn = new RunnableImplsynfn(); 6 new Thread(runnableImplsyn,"一").start(); 7 new Thread(runnableImplsyn,"二").start(); 8 } 9 }
四,问题解决方案之静态synchronize方法:
同上
1 public class RunnableImplsynfn implements Runnable { 2 3 private static int ticket=200; 4 @Override 5 public void run() { 6 statictest(); 7 8 } 9 10 private static synchronized void statictest() { 11 while(true){ 12 if (ticket<=0){ 13 break; 14 } 15 try { 16 Thread.sleep(200); 17 String name = Thread.currentThread().getName(); 18 System.out.println("第"+name+"窗口正在卖出第"+(200-ticket+1)+"张票,剩余"+(--ticket)+"张"); 19 } catch (InterruptedException e) { 20 e.printStackTrace(); 21 } 22 23 } 24 25 } 26 27 28 } 29 }
测试类一样略。
五,问题解决方案之lock方法:
1 import java.util.concurrent.locks.Lock; 2 import java.util.concurrent.locks.ReentrantLock; 3 4 public class Runnablelock implements Runnable { 5 private int ticket=200; 6 Lock l= new ReentrantLock(); 7 8 @Override 9 public void run() { 10 while(true){ 11 /* try { 12 Thread.sleep(20); 13 } catch (InterruptedException e) { 14 e.printStackTrace(); 15 }*/ 16 l.lock();//从此以后加锁 17 18 if (ticket<=0){ 19 break; 20 } 21 try { 22 Thread.sleep(200); 23 24 String name = Thread.currentThread().getName(); 25 System.out.println("第"+name+"窗口正在卖出第"+(200-ticket+1)+"张票,剩余"+(--ticket)+"张"); 26 } catch (InterruptedException e) { 27 e.printStackTrace(); 28 }finally { 29 l.unlock();//释放锁 30 } 31 32 } 33 34 35 } 36 }
测试类:
1 public class lockTicket { 2 3 4 public static void main(String[] args) { 5 Runnablelock runnablelock = new Runnablelock(); 6 new Thread(runnablelock,"一").start(); 7 new Thread(runnablelock,"二").start(); 8 } 9 }