1、什么是“线程安全”与“非线程安全”?
“非线程安全”会在多个线程对同一对象总的实例变量进行并发访问时发生,产生的后果是“脏读”,也就是取到的数据其实是被更改过的。
“线程安全”是以获得的实例变量的值是经过同步处理的,不会出现脏读的现象。
2、非线程安全例子?怎么解决?
非线程安全 |
package com.jvm.thread; public class HasSelfPrivateNum { private int num = 0; public void add(String username){ try{ if(username.equals("a")){ num = 100; System.out.println("a set over!"); Thread.sleep(2000); }else{ num = 200; System.out.println("b set over!"); } System.out.println(username + " num = " + num); }catch(InterruptedException e){ e.printStackTrace(); } } } |
package com.jvm.thread; public class MyThreadA extends Thread { private HasSelfPrivateNum obj; public MyThreadA(HasSelfPrivateNum obj){ this.obj = obj; }
@Override public void run() { super.run(); obj.add("a"); } } |
package com.jvm.thread; public class MyThreadB extends Thread { private HasSelfPrivateNum obj; public MyThreadB(HasSelfPrivateNum obj){ this.obj = obj; }
@Override public void run() { super.run(); obj.add("b"); } } |
package com.jvm.thread; public class MyThread06 { public static void main(String[] args) { HasSelfPrivateNum obj = new HasSelfPrivateNum(); MyThreadA a = new MyThreadA(obj); a.start(); MyThreadB b = new MyThreadB(obj); b.start(); } } |
a set over! b set over! b num = 200 a num = 200 |
解决方法:在 add() 前加关键字synchronized |
a set over! a num = 100 b set over! b num = 200 |
3、synchronized 关键字
当A线程调用anyObject 对象加入synchronized关键字的X方法时,A线程就获得了X方法锁,更准确地讲,获得的是对象的锁,所以,其他线程必须等待A线程执行完毕才能调用X方法,但线程B可以随意调用其他的非synchronized同步方法。
当A线程调用anyObject 对象加入synchronized关键字的X方法时,A线程就获得了X方法所在的对象的锁,所以其他线程必须等待A线程执行完毕才可以调用X方法,而B如果调用声明了synchronized关键字的非X方法时,必须等待A线程将X方法执行完,也就是释放对象锁后才可以调用。
4、synchronized锁重入
“可重入锁”:自己可以再次获得自己的内部锁。可重入锁也支持在父子继承的环境中。
package com.jvm.thread; public class Service extends Thread { synchronized public void service1(){ System.out.println("service1"); service2(); } synchronized public void service2(){ System.out.println("service2"); service3(); } synchronized public void service3(){ System.out.println("service3"); }
@Override public void run() { super.run(); Service service = new Service(); service.service1(); }
public static void main(String[] args) { Service service = new Service(); service.start(); } } |
service1 service2 service3 |
5、锁自动释放——出现异常
当一个线程执行的代码出现异常时,其所持有的锁会自动释放。
6、synchronized同步方法的缺点?解决办法?
Synchronized同步方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个长时间的任务,那么B线程则必须等待比较长时间。
在这样的情况下可以使用synchronized同步语句块来解决。
7、synchronized同步语句块的使用?
当两个并发线程访问同一个对象object中的synchronized(this)同步代码块时,一段时间内只能有一个线程被执行,另外一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
package com.jvm.thread; public class MyTask { public void taskMethod(){ try { synchronized (this) { System.out.println("begin time=" + System.currentTimeMillis()); Thread.sleep(2000); System.out.println("end time=" + System.currentTimeMillis()); } } catch (InterruptedException e) { e.printStackTrace(); } } } |
package com.jvm.thread; public class MyThreadA extends Thread { private MyTask task; public MyThreadA(MyTask task){ this.task = task; }
@Override public void run() { task.taskMethod(); } } |
package com.jvm.thread; public class MyThreadB extends Thread { private MyTask task; public MyThreadB(MyTask task){ this.task = task; }
@Override public void run() { task.taskMethod(); } } |
package com.jvm.thread; public class Run { public static void main(String[] args) { MyTask task = new MyTask(); MyThreadA a = new MyThreadA(task); a.setName("a"); a.start(); MyThreadB b = new MyThreadB(task); b.setName("b"); b.start(); } } |
begin time=1498358631251 end time=1498358633252 begin time=1498358633252 end time=1498358635252 |
8、synchronized代码块间的同步性?
在使用不同synchronized(this)代码块时需要注意的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞,这说明synchronized使用的“对象监视器”是一个。
9、将任意对象作为对象监视器?优点?
使用synchronized(this)格式来同步代码块,其实Java还支持对“任意对象”作为“对象监视器”来实现同步的功能。这个“任意对象”大多数是实例变量及方法的参数,使用格式为synchronized(非this对象)。
优点:如果在一个类中有很多个synchronized方法,这时虽然能实现同步,但是会受到阻塞,所以影响运行效率;但如果使用同步代码块锁非this对象,则synchronized(非this)代码块中程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,则可大大提高效率。
package com.jvm.thread; public class Service extends Thread { private String username; private String password; private String anyString = new String();
public void setUsernamePassword(String username, String pwssword){ try { synchronized (anyString) { System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " go in to synchronized block"); username = username; Thread.sleep(3000); pwssword = pwssword; System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " leave synchronized block"); } } catch (InterruptedException e) { e.printStackTrace(); } } } |
package com.jvm.thread; public class MyThreadA extends Thread { private Service service; public MyThreadA(Service service){ this.service = service; }
@Override public void run() { service.setUsernamePassword("a", "aa"); } } |
package com.jvm.thread; public class MyThreadB extends Thread { private Service service; public MyThreadB(Service service){ this.service = service; }
@Override public void run() { service.setUsernamePassword("b", "bb"); } } |
package com.jvm.thread; public class Run { public static void main(String[] args) { Service service = new Service(); MyThreadA a = new MyThreadA(service); a.setName("A"); a.start(); MyThreadB b = new MyThreadB(service); b.setName("B"); b.start(); } } |
Thread name:A at 1498744309904 go in to synchronized block Thread name:A at 1498744312906 leave synchronized block Thread name:B at 1498744312906 go in to synchronized block Thread name:B at 1498744315906 leave synchronized block |
10、3个结论
“synchronized(非this对象x)”格式的写法是将x对象本身作为“对象监视器”,这样就可以得出以下3个结论:
1)当多个线程同时执行synchronized(x){}同步代码块时呈同步效果。
2)当其他线程执行x对象中synchronized同步方法时呈同步效果。
3)当其他线程执行x对象方法里面的synchronized(this)代码块时也呈同步效果。
原因:使用同一个“对象监视器”。
11、静态同步synchronized方法与synchronized(class)代码块
关键字synchronized还可以应用在static静态方法上,如果这样写,那是对当前的 *.java文件对应的Class类进行持锁。
package com.jvm.thread; public class Service extends Thread { synchronized public static void printA() { try { System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " go in printA()"); Thread.sleep(3000); System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " leave printA()"); } catch (InterruptedException e) { e.printStackTrace(); } }
synchronized public static void printB() { System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " go in printB()"); System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " leave printB()"); } } |
package com.jvm.thread; public class MyThreadA extends Thread { private Service service; public MyThreadA(Service service){ this.service = service; }
@Override public void run() { service.printA(); } } |
package com.jvm.thread; public class MyThreadB extends Thread { private Service service; public MyThreadB(Service service){ this.service = service; }
@Override public void run() { service.printB(); } } |
package com.jvm.thread; public class Run { public static void main(String[] args) { Service service = new Service(); MyThreadA a = new MyThreadA(service); a.setName("A"); a.start(); MyThreadB b = new MyThreadB(service); b.setName("B"); b.start(); } } |
Thread name:A at 1498746369790 go in printA() Thread name:A at 1498746372791 leave printA() Thread name:B at 1498746372792 go in printB() Thread name:B at 1498746372792 leave printB() |
分析:从运行结果来看,和synchronized加到非static方法上使用效果一样。其实有本质上的不同,synchronized加到static静态方法上是给Class类加锁,而synchronized加到非static方法上是给对象加锁。 |
package com.jvm.thread; public class Service extends Thread { synchronized public static void printA() { try { System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " go in printA()"); Thread.sleep(3000); System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " leave printA()"); } catch (InterruptedException e) { e.printStackTrace(); } }
synchronized public static void printB() { System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " go in printB()"); System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " leave printB()"); }
synchronized public void printC() { System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " go in printC()"); System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " leave printC()"); } } |
package com.jvm.thread; public class MyThreadC extends Thread { private Service service; public MyThreadC(Service service){ this.service = service; }
@Override public void run() { service.printC(); } } |
package com.jvm.thread; public class Run { public static void main(String[] args) { Service service = new Service(); MyThreadA a = new MyThreadA(service); a.setName("A"); a.start(); MyThreadB b = new MyThreadB(service); b.setName("B"); b.start(); MyThreadC myThreadC = new MyThreadC(service); myThreadC.setName("C"); myThreadC.start(); } }
|
Thread name:A at 1498746943314 go in printA() Thread name:C at 1498746943315 go in printC() Thread name:C at 1498746943315 leave printC() Thread name:A at 1498746946315 leave printA() Thread name:B at 1498746946315 go in printB() Thread name:B at 1498746946315 leave printB()
|
分析:异步的原因是持有不同的锁,一个是对象锁,另外一个是Class锁,而Class锁可以对类的所有对象实例起作用。 |
package com.jvm.thread; public class Service extends Thread { synchronized public static void printA() { try { System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " go in printA()"); Thread.sleep(3000); System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " leave printA()"); } catch (InterruptedException e) { e.printStackTrace(); } }
synchronized public static void printB() { System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " go in printB()"); System.out.println("Thread name:" + Thread.currentThread().getName() + " at " + System.currentTimeMillis() + " leave printB()"); } } |
package com.jvm.thread; public class Run { public static void main(String[] args) { Service service1 = new Service(); Service service2 = new Service(); MyThreadA a = new MyThreadA(service1); a.setName("A"); a.start(); MyThreadB b = new MyThreadB(service2); b.setName("B"); b.start(); } } |
Thread name:A at 1498747202057 go in printA() Thread name:A at 1498747205057 leave printA() Thread name:B at 1498747205057 go in printB() Thread name:B at 1498747205057 leave printB() |
同步代码块synchronized(class)代码块的作用和synchronized static方法的作用一样。
12、数据类型String的常量池特性
在JVM中具有String常量池缓冲的功能 |
package com.jvm.thread; public class Test { public static void main(String[] args) { String a = "a"; String b = "a"; System.out.println(a == b); //true } } |
将synchronized(string)同步块与String联合使用时,要注意常量池带来的一些例外。
package com.jvm.thread; public class Service extends Thread { public static void print(String str) { try { synchronized (str) { while(true){ System.out.println(Thread.currentThread().getName()); Thread.sleep(1000); } } } catch (InterruptedException e) { e.printStackTrace(); } } } |
package com.jvm.thread; public class MyThreadA extends Thread { private Service service; public MyThreadA(Service service){ this.service = service; }
@Override public void run() { service.print("AA"); } } |
package com.jvm.thread; public class MyThreadB extends Thread { private Service service; public MyThreadB(Service service){ this.service = service; }
@Override public void run() { service.print("AA"); } } |
package com.jvm.thread; public class Run { public static void main(String[] args) { Service service = new Service(); MyThreadA a = new MyThreadA(service); a.setName("A"); a.start(); MyThreadB b = new MyThreadB(service); b.setName("B"); b.start(); } } |
A A A A A A A |
分析:死循环,原因是两个值都是AA两个线程持有相同的锁,所以造成线程B不能执行。这就是String常量池所带来的问题。因此,在大多数情况下,同步synchronized代码块都不适用String作为锁对象,而改用其他,比如 new Object()实例化一个Oject对象。 |
13、同步synchronized方法无限等待与解决
package com.jvm.thread; public class Service extends Thread { synchronized public void methodA() { System.out.println("methodA begin"); boolean isContinueRun = true; while (isContinueRun) { } System.out.println("methodA end"); } synchronized public void methodB() { System.out.println("methodB begin"); System.out.println("methodB end"); } } |
package com.jvm.thread; public class MyThreadA extends Thread { private Service service; public MyThreadA(Service service){ this.service = service; }
@Override public void run() { service.methodA(); } } |
package com.jvm.thread; public class MyThreadB extends Thread { private Service service; public MyThreadB(Service service){ this.service = service; }
@Override public void run() { service.methodB(); } } |
package com.jvm.thread; public class Run { public static void main(String[] args) { Service service = new Service(); MyThreadA a = new MyThreadA(service); a.start(); MyThreadB b = new MyThreadB(service); b.start(); } } |
methodA begin |
分析:线程A不释放锁,线程B永远得不到运行的机会,锁死了。 |
解决:同步代码块 |
package com.jvm.thread; public class Service extends Thread { Object object1 = new Object(); public void methodA() { synchronized (object1) { System.out.println("methodA begin"); boolean isContinueRun = true; while (isContinueRun) {
} System.out.println("methodA end"); } } Object object2 = new Object(); public void methodB() { synchronized (object2) { System.out.println("methodB begin"); System.out.println("methodB end"); } } } |
methodA begin methodB begin methodB end |
14、多线程的死锁
Java线程死锁是一个经典的多线程问题,因为不同的线程都在等待根本不可能释放的锁,从而导致所有的任务都无法继续完成。在多线程技术中心,“死锁”是必需避免的,因为这会造成线程的“假死”。
死锁例子: |
package com.jvm.thread; public class DeadThread 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(username.equals("a")){ synchronized (lock1) { try { System.out.println("username = " + username); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock2) { System.out.println("lock1 -> lock2"); } } }
if(username.equals("b")){ synchronized (lock2) { try { System.out.println("username = " + username); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock1) { System.out.println("lock2 -> lock1"); } } } }
public static void main(String[] args) throws InterruptedException { DeadThread deadThread = new DeadThread(); deadThread.setFlag("a"); Thread thread1 = new Thread(deadThread); thread1.start(); Thread.sleep(1000);
deadThread.setFlag("b"); Thread thread2 = new Thread(deadThread); thread2.start(); } } |
username = a username = b |
注意:死锁的实现与嵌套不嵌套没有关系。 |
1/ valotile 关键字的作用是什么?缺点是什么?
使变量在多个线程间可见。但valotile关键字最致命的缺点是不支持原子性。
package com.jvm.thread; public class PrintString { private boolean isContinuePrint = true;
public void setContinuePrint(boolean isContinuePrint) { this.isContinuePrint = isContinuePrint; }
public boolean isContinuePrint() { return isContinuePrint; }
public void printStringMethod(){ try { while (isContinuePrint) { System.out.println("run printStringMethod threadName=" + Thread.currentThread().getName()); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } }
public static void main(String[] args) { PrintString printString = new PrintString(); printString.printStringMethod(); System.out.println("I will stop it! stopThread=" + Thread.currentThread().getName()); printString.setContinuePrint(false); } } |
run printStringMethod threadName=main run printStringMethod threadName=main run printStringMethod threadName=main run printStringMethod threadName=main run printStringMethod threadName=main run printStringMethod threadName=main run printStringMethod threadName=main run printStringMethod threadName=main run printStringMethod threadName=main run printStringMethod threadName=main |
分析:main线程一直在处理while循环,没办法执行后面的代码。 解决:使用多线程 |
package com.jvm.thread; public class PrintString implements Runnable { private boolean isContinuePrint = true;
public void setContinuePrint(boolean isContinuePrint) { this.isContinuePrint = isContinuePrint; }
public boolean isContinuePrint() { return isContinuePrint; }
public void printStringMethod(){ try { while (isContinuePrint) { System.out.println("run printStringMethod threadName=" + Thread.currentThread().getName()); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } }
@Override public void run() { printStringMethod(); }
public static void main(String[] args) { PrintString printString = new PrintString(); new Thread(printString).start(); System.out.println("I will stop it! stopThread=" + Thread.currentThread().getName()); printString.setContinuePrint(false); } } |
I will stop it! stopThread=main run printStringMethod threadName=Thread-0 |
package com.jvm.thread; public class PrintString implements Runnable { volatile private boolean isContinuePrint = true;
public void setContinuePrint(boolean isContinuePrint) { this.isContinuePrint = isContinuePrint; }
public boolean isContinuePrint() { return isContinuePrint; }
public void printStringMethod(){ try { while (isContinuePrint) { System.out.println("run printStringMethod threadName=" + Thread.currentThread().getName()); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } }
@Override public void run() { printStringMethod(); }
public static void main(String[] args) { PrintString printString = new PrintString(); new Thread(printString).start(); System.out.println("I will stop it! stopThread=" + Thread.currentThread().getName()); printString.setContinuePrint(false); } } |
I will stop it! stopThread=main run printStringMethod threadName=Thread-0 |
使用volatile关键字,强制从公共内存中读取变量的值。
|
2/ 线程安全包含哪些方面?
原子性和可见性。Java的同步机制都是围绕这两个方面来确保线程安全的。
3/关键字synchronized和valotile比较?
a/关键字valotile是线程同步的轻量级实现,因此valotile性能更好。Valotile只能修饰变量,synchronized修饰方法和代码块。
b/多线程访问valotile不会发生阻塞,而synchronized会。
c/valotile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步。
d/valotile解决的是变量在多个线程之间的可见性,而synchronized解决的是多个线程之间访问资源的同步性。
3/valotile非原子性?怎么解决?
package com.jvm.thread; public 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() { addCount(); }
public static void main(String[] args) { MyThread[] myThreadArr = new MyThread[100]; for(int i = 0; i < 100; i++){ myThreadArr[i] = new MyThread(); } for(int i = 0; i < 100; i++){ myThreadArr[i].start(); } } } |
count=100 count=300 count=200 count=400 count=600 count=600 count=700 count=800 count=900 count=1000 count=1100 count=1200 count=1400 count=1300 count=1500 count=1600 count=1700 count=1800 count=2000 count=1900 count=2200 count=2300 count=2200 count=2400 count=2500 count=2600 count=2900 count=2800 count=2700 count=3000 count=3100 count=3200 count=3300 count=3400 count=3600 count=3700 count=3500 count=4100 count=4000 count=3900 count=4300 count=3800 count=4400 count=4600 count=4200 count=4500 count=4800 count=4700 count=6605 count=6605 count=6605 count=6605 count=6605 count=6605 count=6605 count=6605 count=6605 count=6605 count=6605 count=6605 count=6605 count=6605 count=6605 count=6605 count=6605 count=6605 count=6605 count=6705 count=6805 count=7005 count=7005 count=7205 count=7305 count=7405 count=7105 count=7505 count=7605 count=7705 count=7805 count=7905 count=8005 count=8105 count=8205 count=8305 count=8405 count=8505 count=8605 count=8705 count=8805 count=8905 count=9005 count=9105 count=9205 count=9305 count=9405 count=9505 count=9605 count=9705 count=9805 count=9905 |
分析:用图来演示使用关键字valotile时出现非线程安全的原因。 a/ read和load阶段:从主内存复制变量到当前线程工作内存; b/ use和assign阶段:执行代码,改变共享变量值; c/ store和write阶段:用工作内存数据刷新主存对应变量的值。 在多线程环境中,use和assign是多次出现的,但这一操作并不是原子性,也就是在read和assign之后,如果主内存count变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化,导致私有内存和公共内存中的变量不同步,因此,计算出来的结果和预期不一样,也就出现了非线程安全问题。
|
解决:valotile关键字解决的是变量读取时的可见性问题,但无法保证原子性,因此,对于多个线程访问同一实例变量还是需要加锁同步。 |
package com.jvm.thread; public class MyThread extends Thread { public static int count; private synchronized static void addCount(){ for(int i = 0; i < 100; i++){ count++; } System.out.println("count=" + count); }
@Override public void run() { addCount(); }
public static void main(String[] args) { MyThread[] myThreadArr = new MyThread[100]; for(int i = 0; i < 100; i++){ myThreadArr[i] = new MyThread(); } for(int i = 0; i < 100; i++){ myThreadArr[i].start(); } } } |
count=100 count=200 count=300 count=400 count=500 count=600 count=700 count=800 count=900 count=1000 count=1100 count=1200 count=1300 count=1400 count=1500 count=1600 count=1700 count=1800 count=1900 count=2000 count=2100 count=2200 count=2300 count=2400 count=2500 count=2600 count=2700 count=2800 count=2900 count=3000 count=3100 count=3200 count=3300 count=3400 count=3500 count=3600 count=3700 count=3800 count=3900 count=4000 count=4100 count=4200 count=4300 count=4400 count=4500 count=4600 count=4700 count=4800 count=4900 count=5000 count=5100 count=5200 count=5300 count=5400 count=5500 count=5600 count=5700 count=5800 count=5900 count=6000 count=6100 count=6200 count=6300 count=6400 count=6500 count=6600 count=6700 count=6800 count=6900 count=7000 count=7100 count=7200 count=7300 count=7400 count=7500 count=7600 count=7700 count=7800 count=7900 count=8000 count=8100 count=8200 count=8300 count=8400 count=8500 count=8600 count=8700 count=8800 count=8900 count=9000 count=9100 count=9200 count=9300 count=9400 count=9500 count=9600 count=9700 count=9800 count=9900 count=10000 |
4/验证synchronized具有将线程工作内存的私有变量与公共内存中的变量同步的功能?
关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或者代码快。它包含两个特性:互斥性和可见性。同步synchronized不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程,都能看到由同一个锁保护之前所有的修改效果。
package com.jvm.thread; public class Service { private boolean isCoutinueRun = true;
public void runMethod(){ while(isCoutinueRun){
} System.out.println("have stoped!"); }
public void stopMethod(){ isCoutinueRun = false; } } |
package com.jvm.thread; public class ThreadA extends Thread{ private Service service; public ThreadA(Service service) { this.service = service; } @Override public void run() { service.runMethod(); } } |
package com.jvm.thread; public class ThreadB extends Thread{ private Service service; public ThreadB(Service service) { this.service = service; } @Override public void run() { service.stopMethod(); } } |
package com.jvm.thread; public class Run { public static void main(String[] args) throws InterruptedException { Service service = new Service(); ThreadA threadA = new ThreadA(service); threadA.start();
Thread.sleep(1000);
ThreadB threadB = new ThreadB(service); threadB.start(); System.out.println("have start stop commad"); } } |
have start stop commad |
分析:出现死循环,各线程间的数据值没有可视性造成的 |
解决:synchronized可以具有可视性 |
package com.jvm.thread; public class Service { private boolean isCoutinueRun = true;
public void runMethod(){ String anyString = new String(); while(isCoutinueRun){ synchronized (anyString) {
} } System.out.println("have stoped!"); }
public void stopMethod(){ isCoutinueRun = false; } } |
5/总结?
着重“外练互斥,内修可见”,是掌握多线程并发的重要技术。