标签:
为什么要使用多线程编程?什么时候会出现线程安全问题?
本章目录:
1.1.内部变量不存在线程安全问题
1.2.全局变量导致非线程安全
1.3.使用synchronized进行同步
1.4.多个对象多个锁
1.5.脏读
1.6.将任意对象作为对象监视器
1.7.同步synchronized方法无限等待与解决
1.8.多线程死锁
1.9.死循环
2.0.使用关键字volatile更新数据
2.1.使用原子类进行修改操作
2.2.原子类也并不完全安全
1.1.内部变量不存在线程安全问题
当多个线程执行一个方法,方法内部的局部变量并不是临界资源,因为方法是在栈上执行的,而Java栈是线程私有的,因此不会产生线程安全问题。class PrivateObject { public void add(String username) { int num = 0; if ("a".equals(username)) { num = 100; System.out.println("a set over"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } else { num = 200; System.out.println("b set over"); } System.out.println(username + " num = " + num); } } class ThreadA extends Thread{ private PrivateObject object; public ThreadA(PrivateObject object) { super(); this.object = object; } @Override public void run() { super.run(); object.add("a"); } } class ThreadB extends Thread{ private PrivateObject object; public ThreadB(PrivateObject object) { super(); this.object = object; } @Override public void run() { super.run(); object.add("b"); } } public static void main(String[] args) { PrivateObject privateObject = new PrivateObject(); ThreadA threadA = new ThreadA(privateObject); threadA.start(); ThreadB threadB = new ThreadB(privateObject); threadB.start(); } 运行结果: a set over b set over b num = 200 a num = 100
class PrivateObject { int num = 0; public void add(String username) { if ("a".equals(username)) { num = 100; System.out.println("a set over"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } else { num = 200; System.out.println("b set over"); } System.out.println(username + " num = " + num); } } 运行结果: a set over b set over b num = 200 a num = 200
public synchronized void add(String username) { if ("a".equals(username)) { num = 100; System.out.println("a set over"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } else { num = 200; System.out.println("b set over"); } System.out.println(username + " num = " + num); } 在该函数添加了synchronized之后你会发现输出的结果: a set over a num = 100 b set over b num = 200
public static void main(String[] args) { PrivateObject privateObject1 = new PrivateObject(); PrivateObject privateObject2 = new PrivateObject(); ThreadA threadA = new ThreadA(privateObject1); threadA.start(); ThreadB threadB = new ThreadB(privateObject2); threadB.start(); } 创建多个实例让线程执行分成多路,这就是异步处理,我们可以看出不管ThreadA是否执行了耗时ThreadB还是会去执行,由于多个实例执行多个同步方法。结果输出: a set over b set over b num = 200 a num = 100
class Service { private String name = "A"; private String password = "AA"; public synchronized void update(String name, String password){ try { System.out.println("当前线程为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + "进入同步"); this.name = name; Thread.sleep(3000); this.password = password; System.out.println("name = " + name + ", password = " + password); System.out.println("当前线程为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + "离开同步"); } catch (InterruptedException e) { e.printStackTrace(); } } public String select(){ return "name = " + name + ", password = " + password; } } class MyThread extends Thread { Service service; public MyThread(Service service) { super(); this.service = service; } @Override public void run() { super.run(); service.update("B", "BB"); } } public static void main(String[] args) { Service service = new Service(); MyThread myThread = new MyThread(service); myThread.start(); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(service.select()); } 结果: 当前线程为:Thread-0 在 1467706088809进入同步 name = B, password = AA name = B, password = BB 当前线程为:Thread-0 在 1467706091811离开同步
public synchronized String select(){ return "name = " + name + ", password = " + password; } 结果: 当前线程为:Thread-0 在 1467706347098进入同步 name = B, password = BB 当前线程为:Thread-0 在 1467706350105离开同步 name = B, password = BB1.6.将任意对象作为对象监视器
class Service { String string = new String(); public void update(){ synchronized(string){ try { System.out.println("当前线程为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + "进入同步"); Thread.sleep(3000); System.out.println("当前线程为:" + Thread.currentThread().getName() + " 在 " + System.currentTimeMillis() + "离开同步"); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { Service service = new Service(); MyThread myThreadA = new MyThread(service); myThreadA.start(); MyThread myThreadB = new MyThread(service); myThreadB.start(); } 输出结果: 当前线程为:Thread-0 在 1467707013916进入同步 当前线程为:Thread-0 在 1467707016925离开同步 当前线程为:Thread-1 在 1467707016925进入同步 当前线程为:Thread-1 在 1467707019926离开同步
class Service { public synchronized void methodA(){ System.out.println("methodA begin"); boolean isContinueRun = true; while(isContinueRun){ } System.out.println("methodA end"); } public synchronized void methodB(){ System.out.println("methodB begin"); System.out.println("methodB end"); } } class ThreadA extends Thread { private Service service; public ThreadA(Service service) { this.service = service; } @Override public void run() { super.run(); service.methodA(); } } class ThreadB extends Thread { private Service service; public ThreadB(Service service) { this.service = service; } @Override public void run() { super.run(); service.methodB(); } } public static void main(String[] args) { Service service = new Service(); ThreadA threadA = new ThreadA(service); threadA.start(); ThreadB threadB = new ThreadB(service); threadB.start(); } 运行结果是死循环: methodA begin那么如何解决死循环呢?那就是不要使用同一对象监视器,也就是使用异步处理线程
class Service { String a = "a"; public void methodA(){ synchronized (a) { System.out.println("methodA begin"); boolean isContinueRun = true; while(isContinueRun){ } System.out.println("methodA end"); } } String b = "b"; public void methodB(){ synchronized (b) { System.out.println("methodB begin"); System.out.println("methodB end"); } } } 这样就不会造成死锁了。 methodA begin methodB begin methodB end1.8.多线程死锁
class DealThread implements Runnable{ public String username; public Object lock1 = new Object(); public Object lock2 = new Object(); public void setFlag(String username) { this.username = username; } @Override public void run() { if("a".equals(username)){ synchronized (lock1) { System.out.println("username = " + username); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("a-----"); synchronized (lock2) { System.out.println("按lock1 -> lock2代码顺序执行了"); } } } if("b".equals(username)){ synchronized (lock2) { System.out.println("username = " + username); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("b-----"); synchronized (lock1) { System.out.println("按lock2 -> lock1代码顺序执行了"); } } } } } public static void main(String[] args) { DealThread t1 = new DealThread(); t1.setFlag("a"); Thread thread1 = new Thread(t1); thread1.start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } t1.setFlag("b"); Thread thread2 = new Thread(t1); thread2.start(); } 运行结果: username = a username = b a----- b-----线程1执行了a判断进入锁,锁了lock1对象进入耗时操作,另外一个线程2进入锁了lock2对象也进入了耗时操作,这时,lock1耗时已完毕执行了lock2操作,然而lock2对象还没有解锁,所以线程1被锁死无法释放而继续等待,这时线程2进入lock1对象锁,而lock1却已经发生死锁无法释放,这种情况之下两锁无法释放就会进入死锁状态。
class PrintString { private boolean isContinuePrint = true; public boolean isContinuePrint() { return isContinuePrint; } public void setContinuePrint(boolean isContinuePrint) { this.isContinuePrint = isContinuePrint; } public void printStrngMethod() { try { while (isContinuePrint) { System.out.println(isContinuePrint); System.out.println("run printStringMethod threadName = " + Thread.currentThread().getName()); Thread.sleep(1000); } } catch (Exception e) { } } } public static void main(String[] args) { PrintString printString = new PrintString(); printString.printStrngMethod(); System.out.println("我要停止它!stopThread=" + Thread.currentThread().getName()); printString.setContinuePrint(false); } 运行结果: true run printStringMethod threadName = main true run printStringMethod threadName = main true run printStringMethod threadName = main true run printStringMethod threadName = main
public void setContinuePrint(boolean isContinuePrint) { this.isContinuePrint = isContinuePrint; }如果在多线程中执行了死循环没有加volatile关键字就算你在主线程
public void setContinuePrint(boolean isContinuePrint) { this.isContinuePrint = isContinuePrint; }设置了false,也是没有用的,因为你更新的是公有内存的栈,而私有的内存栈没有被修改,所以会出现死循环不停止。那么如何将公有内存的栈去更新私有的栈,去同步数据呢?在私有内存前加volatile关键字。
class MyThread extends Thread{ volatile public static int count; private static void addCount(){ for (int i = 0; i < 100; i++) { count++; } System.out.println("count=" + count); } @Override public void run() { super.run(); addCount(); } } public static void main(String[] args) { MyThread[] myThreads = new MyThread[100]; for (int i = 0; i < myThreads.length; i++) { myThreads[i] = new MyThread(); } for (int i = 0; i < myThreads.length; i++) { myThreads[i].start(); } } 运行结果: count=8924 count=9024 count=9124 count=9224 count=9324 count=9424 count=9524 count=9624 count=9724 count=9824 count=9924值出现了脏读了,可见volatile不能同步函数,那么如何解决这个问题呢?因为要同步函数,那么在函数前加synchronized。
class AddCountThread extends Thread { private AtomicInteger count = new AtomicInteger(0); @Override public void run() { super.run(); for (int i = 0; i < 100; i++) { System.out.println(count.incrementAndGet()); } } } public static void main(String[] args) { AddCountThread countThread = new AddCountThread(); Thread t1 = new Thread(countThread); t1.start(); Thread t2 = new Thread(countThread); t2.start(); Thread t3 = new Thread(countThread); t3.start(); Thread t4 = new Thread(countThread); t4.start(); Thread t5 = new Thread(countThread); t5.start(); } 运行结果: 492 493 494 495 496 497 498 499 500这样值一样不会出现脏读现象了。
class MyService { public AtomicLong atomicLong = new AtomicLong(); public void addNum() { System.out.println(Thread.currentThread().getName() + "加了100之后的值就是:" + atomicLong.addAndGet(100)); atomicLong.addAndGet(1); } } class MyThread extends Thread { MyService myService; public MyThread(MyService myService) { this.myService = myService; } @Override public void run() { super.run(); myService.addNum(); } } public static void main(String[] args) { try { MyService service = new MyService(); MyThread[] myThreads = new MyThread[10]; for (int i = 0; i < myThreads.length; i++) { myThreads[i] = new MyThread(service); } for (int i = 0; i < myThreads.length; i++) { myThreads[i].start(); } Thread.sleep(1000); System.out.println(service.atomicLong.get()); } catch (Exception e) { } } 运行结果: Thread-0加了100之后的值就是:100 Thread-3加了100之后的值就是:300 Thread-1加了100之后的值就是:200 Thread-5加了100之后的值就是:403 Thread-7加了100之后的值就是:504 Thread-9加了100之后的值就是:605 Thread-2加了100之后的值就是:706 Thread-4加了100之后的值就是:807 Thread-6加了100之后的值就是:908 Thread-8加了100之后的值就是:1009 1010值一样出现脏读现象,那么如何解决问题呢?一样使用synchronized关键字同步数据
public synchronized void addNum() { System.out.println(Thread.currentThread().getName() + "加了100之后的值就是:" + atomicLong.addAndGet(100)); atomicLong.addAndGet(1); } 运行结果: Thread-1加了100之后的值就是:100 Thread-3加了100之后的值就是:201 Thread-5加了100之后的值就是:302 Thread-7加了100之后的值就是:403 Thread-9加了100之后的值就是:504 Thread-0加了100之后的值就是:605 Thread-2加了100之后的值就是:706 Thread-4加了100之后的值就是:807 Thread-6加了100之后的值就是:908 Thread-8加了100之后的值就是:1009 1010关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法或某一个代码块。它包含两个特征:互斥性和可见性。同步synchronized不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护之前所有的修改效果。
标签:
原文地址:http://blog.csdn.net/aaaaaaaaabaas/article/details/51861670