线程安全
1、线程局部变量的共享
定义线程安全的静态变量ConcurrentHashMap 存储线程对象的局部变量
public class Test01 { public static ConcurrentHashMap<Thread, Integer> map = new ConcurrentHashMap<>(); public static void main(String[] args) { /** * 知识点:线程局部变量的共享 * * 共享一个变量的情况 */ new Thread(new Runnable() { @Override public void run() { int i = 10; //存数据 map.put(Thread.currentThread(), i); A a = new A(); B b = new B(); a.println();//10 b.println();//10 } },"线程1").start(); new Thread(new Runnable() { @Override public void run() { int i = 20; //存数据 map.put(Thread.currentThread(), i); A a = new A(); B b = new B(); a.println();//20 b.println();//20 } },"线程2").start(); } } public class A { //public class B { public void println(){ Thread thread = Thread.currentThread(); Integer value = Test01.map.get(thread); System.out.println(thread.getName() + "中的A类对象调用了println() -- " + value); // System.out.println(thread.getName() + "中的B类对象调用了println() -- " + value); } }
共享多个变量的情况 : 定义数据包类存储
//数据包类 public class Data { private int i; private String str; } public class Test01 { public static ConcurrentHashMap<Thread, Data> map = new ConcurrentHashMap<>(); public static void main(String[] args) { /** * 知识点:线程局部变量的共享 * * 共享多个变量的情况 */ new Thread(new Runnable() { @Override public void run() { Data data = new Data(10,"aaa"); //存数据 map.put(Thread.currentThread(), data); A a = new A(); B b = new B(); a.println();//10 b.println();//10 } },"线程1").start(); new Thread(new Runnable() { @Override public void run() { Data data = new Data(20,"bbb"); //存数据 map.put(Thread.currentThread(), data); A a = new A(); B b = new B(); a.println();//20 b.println();//20 } },"线程2").start(); } }
共享多个变量的情况 - ThreadLocal
public class Test01 { public static ThreadLocal<Data> local = new ThreadLocal<>(); public static void main(String[] args) { /** * 知识点:线程局部变量的共享 * * 共享多个变量的情况 - ThreadLocal */ new Thread(new Runnable() { @Override public void run() { Data data = Data.getInstance(10,"aaa"); //存数据 local.set(data); A a = new A(); B b = new B(); a.println();//10 b.println();//10 } },"线程1").start(); new Thread(new Runnable() { @Override public void run() { Data data = Data.getInstance(20,"bbb"); //存数据 local.set(data); A a = new A(); B b = new B(); a.println();//20 b.println();//20 } },"线程2").start(); } } public class B { public void println(){ Thread thread = Thread.currentThread(); Data value = Test01.local.get(); System.out.println(thread.getName() + "中的B类对象调用了println() -- " + value); } }
2、需求分析
铁道部发布了一个售票任务,要求销售1000张票,要求有3个窗口来进行销售,
请编写多线程程序来模拟这个效果public class MyThread extends Thread{ private static int ticket = 1000; private static Object obj = new Object(); public MyThread(String name) { super(name); } @Override public void run() { while(ticket > 0){ //synchronized("abc"){ //synchronized(Character.class){ synchronized(obj){ if(ticket > 0){ System.out.println(Thread.currentThread().getName() + "正在销售第" + ticket + "张票"); ticket--; } if(ticket <= 0){ System.out.println(Thread.currentThread().getName() + "票已售完"); } } } } } public class Test01 { public static void main(String[] args) { MyThread t1 = new MyThread("窗口001"); MyThread t2 = new MyThread("窗口002"); MyThread t3 = new MyThread("窗口003"); t1.start(); t2.start(); t3.start(); } }
问题1:三个窗口都卖了1000张票,一共卖了3000张
出现原因:三个线程调用三次run方法,就有3*1000张票
解决方案:三个线程共用同一个票的变量问题2:有些票卖了重票
出现原因:票的输出语句输出后,还没有来得及做票的减减,就被其他线程抢到CPU资源了
解决方案:票的输出语句 和 票的减减必须同时执行完毕后,才能被其他线程抢到CPU资源了 - 加锁问题3:出现负数
出现原因: 票到了零界点(ticket=1),三个线程都进入循环中
解决方案:锁中再判断一次锁对象:多个线程要想互斥住,就必须使用同一把锁
synchronized修饰方法
public class MyThread extends Thread{ private static int ticket = 1000; public MyThread(String name) { super(name); } @Override public void run() { while(ticket > 0){ method02(); } } //锁对象:类的字节码文件对象(MyThread.class) public static synchronized void method02(){ if(ticket > 0){ System.out.println(Thread.currentThread().getName() + "正在销售第" + ticket + "张票"); ticket--; } if(ticket <= 0){ System.out.println(Thread.currentThread().getName() + "票已售完"); } } //锁对象:this public synchronized void method01(){ if(ticket > 0){ System.out.println(Thread.currentThread().getName() + "正在销售第" + ticket + "张票"); ticket--; } if(ticket <= 0){ System.out.println(Thread.currentThread().getName() + "票已售完"); } } }
1.synchronized
同步代码块:
synchronized(锁对象){//自动上锁
…要互斥住的代码…
}//自动解锁
同步方法:
2.Lockpublic class MyThread extends Thread{ private static int ticket = 1000; private static Lock lock = new ReentrantLock(); public MyThread(String name) { super(name); } @Override public void run() { while(ticket > 0){ lock.lock();//手动上锁 if(ticket > 0){ System.out.println(Thread.currentThread().getName() + "正在销售第" + ticket + "张票"); ticket--; } if(ticket <= 0){ System.out.println(Thread.currentThread().getName() + "票已售完"); } lock.unlock();//手动解锁 } } }
任务Task : 可以不同线程共用对象
public class Task implements Runnable{ private int ticket = 1000; private static Object obj1 = new Object(); private Object obj2 = new Object(); @Override public void run() { while(ticket > 0){ // synchronized("abc"){ // synchronized(Byte.class){ // synchronized(obj1){ // synchronized(obj2){ synchronized(this){ if(ticket > 0){ System.out.println(Thread.currentThread().getName() + "正在销售第" + ticket + "张票"); ticket--; } if(ticket <= 0){ System.out.println(Thread.currentThread().getName() + "票已售完"); } } } } } public class Test01 { public static void main(String[] args) { Task task = new Task(); Thread t1 = new Thread(task, "窗口001"); Thread t2 = new Thread(task, "窗口002"); Thread t3 = new Thread(task, "窗口003"); t1.start(); t2.start(); t3.start(); } }
死锁:锁的嵌套使用可能会导致死锁,应尽量避免