线程安全问题产生的原因:多个线程在操作共享的数据。
解决思路;
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程时不可以参与运算的。必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。
synchronized(对象)
{
需要被同步的代码 ;
}
1、多个窗口同时卖票,保证票数不<=0
public class MyThread { public static void main(String[] args) throws InterruptedException { Ticket ticket = new MyThread().new Ticket(); //共享数据和操作共享数据的方法最好放在一个类中,这样加锁方便 for (int i = 0; i < 4; i++) {//开启4个窗口同时卖票 new Thread(ticket).start(); } } class Ticket implements Runnable//extends Thread { private int num = 1000;//操作共享数据 Object obj = new Object();//只new一次才能保证锁的唯一性 public void run() { while(true) { if(num>0) { synchronized(obj) { if(num>0) { try{Thread.sleep(10);}catch (InterruptedException e){} System.out.println(Thread.currentThread().getName()+".....sale...."+num--); } } } } } } }
2、加在方法上的synchronized的区别,加在静态方法上的是当前类名的字节码,加在实例方法上的是当前的实例对象this:
class Test { synchronized static void sayHello3()//锁为Test.class { } synchronized void getX(){}//锁为实例化后new t=new Test()的t }
3、简述synchronized和java.util.concurrent.locks.Lock的异同 ?
主要相同点:Lock能完成synchronized所实现的所有功能
主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。Lock还有更强大的功能,
private class Subtractor implements Runnable { @Override public void run() { // TODO Auto-generated method stub while(true) { /*synchronized (ThreadTest.this) { System.out.println("j--=" + j--); //这里抛异常了,synchronized 也会自动释放锁 }*/ lock.lock(); try { System.out.println("j--=" + j--); }finally { lock.unlock(); //lock不能自动释放锁,所以必须在try-finally中手动释放 } } } }
4、设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1(不考虑增加减少的顺序问题)。
public class TwoThreadNoSequence { public static void main(String[] args) { final Mydata mydata = new TwoThread().new Mydata(); for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { while(true){ mydata.add(); } } }).start(); new Thread(new Runnable() { @Override public void run() { while(true){ mydata.sub(); } } }).start(); } } class Mydata { private int j=0; public synchronized void add() { j++; } public synchronized void sub() { j--; } } }
5、设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1(考虑增加减少的顺序问题,先增加2次,再减少1次)。
此题涉及的坑有:
a、由于是4个线程,所以唤醒机制绝不能是单个唤醒,只能全部唤醒,不然会让其他陷入等待的线程无法获得执行权,程序会陷入死锁
b、由于是使用Conditon,所以Condition的使用得在lock方法调用之后,因为其是基于lock实例化出的condition,且使用方法得注意,由于Object上有wait和notify,很容易就会使用到Object上的方法,但是这个和Condition是不搭的,使用错误会报错,Condition上的是await和singal,singalAll
c、输出两次,得控制代码,千万不要有控制线程的思想,比如多两个加的线程就以为搞定了,这是错误使用多线程,线程执行时机是无须和竞争关系,人为控制不住
public class TwoThreadSequence { public static void main(String[] args) throws InterruptedException { final Mydata mydata = new Mydata(); for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { while(true){ mydata.add(); } } }).start(); new Thread(new Runnable() { @Override public void run() { while(true){ mydata.sub(); } } }).start(); } } static class Mydata { private int j=0; Lock lock=new ReentrantLock(); Condition condition = lock.newCondition(); boolean flag=false; public void add() { lock.lock(); try{ while(flag) //while循环为了防止虚假唤醒 { try { condition.await();//是await不是wait,wait是和synchronized搭配的 //await方法基于lock,所以得位于lock方法调用后面,就如synchronized的锁必须在锁内wait,且是同一把锁 } catch (InterruptedException e) { e.printStackTrace(); } } for (int i = 0; i < 2; i++) { j++; //要控制联系加两次,只能控制数据,千万不要试图去控制线程给你执行两次,那是不现实的,连CPU都无法办到,更何况自己指定线程来连续执行两次 } System.out.println("add: "+j); flag=true; condition.signalAll();//千万不要调用notify方法,那个是和synchronized搭配的,与condition不搭 } finally { lock.unlock(); } } public void sub() { lock.lock(); try{ while(!flag) { try { condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } j--; System.out.println("sub: "+j); flag=false; condition.signalAll(); } finally { lock.unlock(); } } } }
6、3个线程同时运行,a运行完唤醒c,c运行完唤醒b,b运行完唤醒a,如此往复运行,此题就涉及到多个唤醒条件了
public class TwoThreadSequence { public static void main(String[] args) throws InterruptedException { final Mydata mydata = new Mydata(); new Thread(new Runnable() { @Override public void run() { while(true){ mydata.executeA(); } } },"a").start(); new Thread(new Runnable() { @Override public void run() { while(true){ mydata.executeB(); } } },"b").start(); new Thread(new Runnable() { @Override public void run() { while(true){ mydata.executeC(); } } },"c").start(); } static class Mydata { final Lock lock=new ReentrantLock(); final Condition conditionA = lock.newCondition(); final Condition conditionB = lock.newCondition(); final Condition conditionC = lock.newCondition(); String flag="A"; public void executeA() { lock.lock(); try { while(!flag.equals("A")) { try { conditionA.await(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+ "执行了....."); flag="B"; conditionB.signalAll(); } finally { lock.unlock(); } } public void executeB() { lock.lock(); try { while(!flag.equals("B")) { try { conditionB.await(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+ "执行了....."); flag="C"; conditionC.signalAll(); } finally { lock.unlock(); } } public void executeC() { lock.lock(); try { while(!flag.equals("C")) { try { conditionC.await(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+ "执行了....."); flag="A"; conditionA.signalAll(); } finally { lock.unlock(); } } } }