线程安全问题产生的原因:多个线程在操作共享的数据。
解决思路;
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程时不可以参与运算的。必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。
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();
}
}
}
}