在JVM中具备String常量池缓存的功能。java
public class Service { public static void print(String str){ try { synchronized (str) { while (true) { System.out.println(Thread.currentThread().getName()); Thread.sleep(500); } } } catch (Exception e) { e.printStackTrace(); } } }
public class ThreadA extends Thread { @Override public void run() { Service.print("AA"); } } public class ThreadB extends Thread { @Override public void run() { Service.print("AA"); } }
public class Test { public static void main(String[] args) { ThreadA a = new ThreadA(); a.setName("A"); ThreadB b = new ThreadB(); b.setName("B"); a.start(); b.start(); } }
控制台打印结果以下:编程
... A A A A A A ...
出现这种状况就是由于Sting的两个值都是AA,两个线程持有相同的锁,因此形成线程B不能执行。所以在大多数状况下,同步synchronized代码块都不实用String做为锁对象,而改用其余,好比new Object()实例化一个Object对象,但他并不放入缓存中。缓存
public class Service { synchronized public void methodA(){ System.out.println("methodA begin..."); boolean condition = true; while (condition) { } System.out.println("methodA end..."); } synchronized public void methodB(){ System.out.println("methodB begin..."); System.out.println("methodB end..."); } }
public class ThreadA extends Thread { private Service service; public ThreadA(Service service) { super(); this.service = service; } @Override public void run() { service.methodA(); } } public class ThreadB extends Thread { private Service service; public ThreadB(Service service) { super(); this.service = service; } @Override public void run() { service.methodB(); } }
public class Run { public static void main(String[] args) { Service service = new Service(); ThreadA a = new ThreadA(service); a.setName("A"); ThreadB b = new ThreadB(service); b.setName("B"); a.start(); b.start(); } }
控制台打印结果以下:安全
methodA begin...
线程A处于死循环状态,线程B永远没法拿到Service对象锁而一直得不到运行。服务器
对Service对象作以下修改:多线程
public class Service { Object object1 = new Object(); Object object2 = new Object(); public void methodA() { synchronized (object1) { System.out.println("methodA begin..."); boolean condition = true; while (condition) { } System.out.println("methodA end..."); } } public void methodB() { synchronized (object2) { System.out.println("methodB begin..."); System.out.println("methodB end..."); } } }
此时控制台打印结果以下:app
methodA begin... methodB begin... methodB end...
methodA()和methodB()对不一样的对象加锁,因此线程A持有的锁不会对线程B形成影响。异步
Java线程死锁是一个经典的多线程问题,由于不一样的线程都在等待根本不可能被释放的锁,从而致使全部的任务都没法继续完成。ide
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 (Exception 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 (Exception e) { e.printStackTrace(); } synchronized (lock1) { System.out.println("按lock2->lock1代码顺序执行了"); } } } } public static void main(String[] args) { try { DeadThread t1 = new DeadThread(); t1.setFlag("a"); Thread thread1 = new Thread(t1); thread1.start(); Thread.sleep(200); t1.setFlag("b"); Thread thread2 = new Thread(t1); thread2.start(); } catch (Exception e) { e.printStackTrace(); } } }
控制台打印结果以下:性能
username=a username=b
此时程序不结束,处于死锁状态。
能够使用jps命令查看当前线程的id,而后使用jstack -l id来检查是否存在死锁。
//内置类 public class PublicClass { private String username; private String password; class PrivateClass{ private String age; private String address; public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public void printPublicProperty() { System.out.println(username + "-" + password); } } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public static void main(String[] args) { PublicClass publicClass = new PublicClass(); publicClass.setUsername("admin"); publicClass.setPassword("123456"); System.out.println(publicClass.getUsername() + "-" + publicClass.getPassword()); PrivateClass privateClass = publicClass.new PrivateClass(); privateClass.setAddress("shanghai"); privateClass.setAge("25"); System.out.println(privateClass.getAddress() + "-" + privateClass.getAge()); privateClass.printPublicProperty(); } }
控制台打印结果以下:
admin-123456 shanghai-25 admin-123456
//静态内置类 public class PublicClass { static String username; static String password; static class PrivateClass{ private String age; private String address; public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public void printPublicProperty() { System.out.println(username + "-" + password); } } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public static void main(String[] args) { PublicClass publicClass = new PublicClass(); publicClass.setUsername("admin"); publicClass.setPassword("123456"); System.out.println(publicClass.getUsername() + "-" + publicClass.getPassword()); PrivateClass privateClass = new PrivateClass(); privateClass.setAddress("shanghai"); privateClass.setAge("25"); System.out.println(privateClass.getAddress() + "-" + privateClass.getAge()); privateClass.printPublicProperty(); } }
控制台打印结果同上。
public class OutClass { static class Inner{ public void method1() { synchronized ("其余的锁") { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "i=" + i); try { Thread.sleep(100); } catch (Exception e) { } } } } public synchronized void method2() { for (int i = 11; i < 20; i++) { System.out.println(Thread.currentThread().getName() + "i=" + i); try { Thread.sleep(100); } catch (Exception e) { } } } } public static void main(String[] args) { final Inner inner = new Inner(); Thread t1 = new Thread(new Runnable() { @Override public void run() { inner.method1(); } }, "A"); Thread t2 = new Thread(new Runnable() { @Override public void run() { inner.method2(); } }, "B"); t1.start(); t2.start(); } }
控制台打印结果以下:
Ai=0 Bi=11 Bi=12 Ai=1 Bi=13 Ai=2 Ai=3 Bi=14 Ai=4 Bi=15 Bi=16 Ai=5 Ai=6 Bi=17 Ai=7 Bi=18 Bi=19 Ai=8 Ai=9
因为持有不一样的对象监视器,因此打印结果是乱序的。
public class OutClass { static class InnerClass1{ public void method1(InnerClass2 class2) { String threadName = Thread.currentThread().getName(); synchronized (class2) { System.out.println(threadName + "进入InnerClass1的method1方法"); for (int i = 0; i < 5; i++) { System.out.println("i=" + i); try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } } System.out.println(threadName + "离开InnerClass1的method1方法"); } } public synchronized void method2() { String threadName = Thread.currentThread().getName(); System.out.println(threadName + "进入InnerClass1的method2方法"); for (int j = 0; j < 5; j++) { System.out.println("j=" + j); try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } } System.out.println(threadName + "离开InnerClass1的method2方法"); } } static class InnerClass2{ public synchronized void method1() { String threadName = Thread.currentThread().getName(); System.out.println(threadName + "进入InnerClass2的method1方法"); for (int k = 0; k < 5; k++) { System.out.println("k=" + k); try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } } System.out.println(threadName + "离开InnerClass2的method1方法"); } } public static void main(String[] args) { final InnerClass1 class1 = new InnerClass1(); final InnerClass2 class2 = new InnerClass2(); Thread t1 = new Thread(new Runnable() { @Override public void run() { class1.method1(class2); } }, "T1"); Thread t2 = new Thread(new Runnable() { public void run() { class1.method2(); } }, "T2"); Thread t3 = new Thread(new Runnable() { @Override public void run() { class2.method1(); } }, "T3"); t1.start(); t2.start(); t3.start(); } }
控制台打印结果以下:
T2进入InnerClass1的method2方法 T1进入InnerClass1的method1方法 j=0 i=0 j=1 i=1 i=2 j=2 j=3 i=3 i=4 j=4 T2离开InnerClass1的method2方法 T1离开InnerClass1的method1方法 T3进入InnerClass2的method1方法 k=0 k=1 k=2 k=3 k=4 T3离开InnerClass2的method1方法
同步代码块synchronized (class2)对class2上锁后,其余线程只能以同步方式调用class2中的静态同步方法。
public class MyService { private String lock = "123"; public void testMethod() { try { synchronized (lock) { System.out.println(Thread.currentThread().getName() + " begin " + System.currentTimeMillis()); lock = "456"; Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + " end " + System.currentTimeMillis()); } } catch (Exception e) { e.printStackTrace(); } } } public class ThreadA extends Thread { private MyService service; public ThreadA(MyService service) { super(); this.service = service; } @Override public void run() { service.testMethod(); } } public class ThreadB extends Thread { private MyService service; public ThreadB(MyService service) { super(); this.service = service; } @Override public void run() { service.testMethod(); } } public class Run1 { public static void main(String[] args) throws InterruptedException { MyService service = new MyService(); ThreadA a = new ThreadA(service); a.setName("A"); ThreadB b = new ThreadB(service); b.setName("B"); a.start(); Thread.sleep(100); b.start(); } }
k控制台打印结果以下:
A begin 1465980925627 B begin 1465980925727 A end 1465980927627 B end 1465980927727
从打印结果看,A线程和B线程是以异步方式执行的,可见A线程与B线程持有的锁不一样。
将以上main方法中的代码作以下修改:
public class Run1 { public static void main(String[] args) throws InterruptedException { MyService service = new MyService(); ThreadA a = new ThreadA(service); a.setName("A"); ThreadB b = new ThreadB(service); b.setName("B"); a.start(); //Thread.sleep(100); b.start(); } }
此时打印结果以下:
A begin 1465981162126 A end 1465981164127 B begin 1465981164128 B end 1465981166128
可见此时A线程和B线程是以同步方式执行的,A线程和B线程共同争抢的锁是“123”。
PS:只要对象不变,即便对象的属性改变,运行的结果仍是同步的。将以上代码作以下修改:
public class MyService { private StringBuilder lock = new StringBuilder("123"); public void testMethod() { try { synchronized (lock) { System.out.println(Thread.currentThread().getName() + " begin " + System.currentTimeMillis()); lock.append("456"); Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + " end " + System.currentTimeMillis()); } } catch (Exception e) { e.printStackTrace(); } } } public class Run1 { public static void main(String[] args) throws InterruptedException { MyService service = new MyService(); ThreadA a = new ThreadA(service); a.setName("A"); ThreadB b = new ThreadB(service); b.setName("B"); a.start(); Thread.sleep(100); b.start(); } }
此时控制台打印结果以下:
A begin 1465981411980 A end 1465981413980 B begin 1465981413980 B end 1465981415981
可见线程A和线程B是以同步方式执行的。
死循环例子
public class PrintString { private boolean isContinuePrint = true; public boolean isContinuePrint() { return isContinuePrint; } public void setContinuePrint(boolean isContinuePrint) { this.isContinuePrint = isContinuePrint; } public void printStringMethod() { try { while (isContinuePrint) { System.out.println("printStringMethod is running...threadName=" + Thread.currentThread().getName()); Thread.sleep(1000); } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { PrintString printString = new PrintString(); printString.printStringMethod(); System.out.println("中止线程..."); printString.setContinuePrint(false); } }
控制台打印结果以下:
printStringMethod is running...threadName=main printStringMethod is running...threadName=main printStringMethod is running...threadName=main printStringMethod is running...threadName=main printStringMethod is running...threadName=main ......
main线程在printString.printStringMethod()中陷入了死循环,后面的printString.setContinuePrint(false)得不到执行。
修改上面的代码
public class PrintString implements Runnable { private boolean isContinuePrint = true; public boolean isContinuePrint() { return isContinuePrint; } public void setContinuePrint(boolean isContinuePrint) { this.isContinuePrint = isContinuePrint; } public void printStringMethod() { try { while (isContinuePrint) { System.out.println("printStringMethod is running...threadName=" + Thread.currentThread().getName()); Thread.sleep(1000); } } catch (Exception e) { e.printStackTrace(); } } @Override public void run() { printStringMethod(); } public static void main(String[] args) throws InterruptedException { PrintString printString = new PrintString(); new Thread(printString).start(); Thread.sleep(5000); System.out.println("中止线程..."); printString.setContinuePrint(false); } }
此时控制台打印结果以下:
printStringMethod is running...threadName=Thread-0 printStringMethod is running...threadName=Thread-0 printStringMethod is running...threadName=Thread-0 printStringMethod is running...threadName=Thread-0 printStringMethod is running...threadName=Thread-0 中止线程...
此时main线程设置isContinuePrint=false,能够使另外一个线程中止执行。
++注:《Java多线程编程核心技术》P120讲将上面的代码运行在-server服务器模式中的64bit的JVM上时,会出现死循环。实际测试并未出现死循环,暂未弄清缘由。++
package com.umgsai.thread22; public class RunThread extends Thread { volatile private boolean isRunning = true; public boolean isRunning() { return isRunning; } public void setRunning(boolean isRunning) { this.isRunning = isRunning; } @Override public void run() { System.out.println("进入run方法..."); while (isRunning) { System.out.println("running...."); try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } } System.out.println("线程被中止..."); } public static void main(String[] args) { try { RunThread runThread = new RunThread(); runThread.start(); Thread.sleep(1000); runThread.setRunning(false); System.out.println("已将isRunning设置为false"); } catch (Exception e) { e.printStackTrace(); } } }
控制台打印结果以下:
进入run方法... running.... running.... 已将isRunning设置为false 线程被中止...
==使用volatile关键字强制从公共内存中读取变量==。
使用volatile关键字增长了实例变量在多个线程之间的可见性,可是volatile关键字不支持原子性。
synchronized和volatile的比较
public class VolatileTest 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) { VolatileTest[] volatileTests = new VolatileTest[100]; for (int i = 0; i < 100; i++) { volatileTests[i] = new VolatileTest(); } for (int i = 0; i < 100; i++) { volatileTests[i].start(); } } }
控制台打印结果以下:
...... count=5332 count=5232 count=5132 count=5032 count=4932 count=4854 count=4732 count=4732
将以上代码中的addCount方法加上synchronized关键字
public class VolatileTest extends Thread { volatile public static int count; //必定要加static关键字,这样synchronized与static锁的内容就是VolatileTest类了,也就达到同步效果了。 synchronized 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) { VolatileTest[] volatileTests = new VolatileTest[100]; for (int i = 0; i < 100; i++) { volatileTests[i] = new VolatileTest(); } for (int i = 0; i < 100; i++) { volatileTests[i].start(); } } }
此时控制台打印结果以下:
...... count=9300 count=9400 count=9500 count=9600 count=9700 count=9800 count=9900 count=10000
i++操做除了使用synchronized关键字同步外,还能够使用AtomicInteger原子类实现。
import java.util.concurrent.atomic.AtomicInteger; public class AtomicIntegerTest extends Thread { private AtomicInteger count = new AtomicInteger(0); @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(count.incrementAndGet()); } } public static void main(String[] args) { AtomicIntegerTest atomicIntegerTest = new AtomicIntegerTest(); Thread t1 = new Thread(atomicIntegerTest); Thread t2 = new Thread(atomicIntegerTest); Thread t3 = new Thread(atomicIntegerTest); Thread t4 = new Thread(atomicIntegerTest); Thread t5 = new Thread(atomicIntegerTest); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }
控制台打印结果以下:
...... 4992 4993 4994 4995 4996 4997 4998 4999 5000
5个线程成功累加到5000。
public class MyService { public static AtomicLong atomicLong = new AtomicLong(); public void addNum() { System.out.println(Thread.currentThread().getName() + " 加了100以后是:" + atomicLong.addAndGet(100)); atomicLong.addAndGet(1); } } public class MyThread extends Thread { private MyService myService; public MyThread(MyService myService) { this.myService = myService; } @Override public void run() { myService.addNum(); } } public class Run { public static void main(String[] args) { try { MyService myService = new MyService(); MyThread[] array = new MyThread[100]; for (int i = 0; i < array.length; i++) { array[i] = new MyThread(myService); } for (int i = 0; i < array.length; i++) { array[i].start();; } Thread.sleep(1000); System.out.println(myService.atomicLong.get()); } catch (Exception e) { e.printStackTrace(); } } }
控制台打印结果以下:
...... Thread-89 加了100以后是:8987 Thread-96 加了100以后是:9493 Thread-80 加了100以后是:9594 Thread-94 加了100以后是:9695 Thread-97 加了100以后是:9796 Thread-95 加了100以后是:9896 Thread-98 加了100以后是:9998 Thread-99 加了100以后是:10098 10100
累加的结果是正确的,可是打印顺序的错的,这是由于虽然addAndGet方法是原子的,可是方法和方法之间的调用却不是原子的。
对以上代码作以下修改:
public class MyService { public static AtomicLong atomicLong = new AtomicLong(); synchronized public void addNum() { System.out.println(Thread.currentThread().getName() + " 加了100以后是:" + atomicLong.addAndGet(100)); atomicLong.addAndGet(1); } }
此时控制台打印结果以下:
...... Thread-86 加了100以后是:9392 Thread-87 加了100以后是:9493 Thread-88 加了100以后是:9594 Thread-92 加了100以后是:9695 Thread-93 加了100以后是:9796 Thread-94 加了100以后是:9897 Thread-95 加了100以后是:9998 Thread-98 加了100以后是:10099 10100
此时线程以同步方式执行addNum方法,每次先加100再加1.
关键字synchronized能够使多个线程访问同一个资源具备同步性,并且还能够将线程工做内存中的私有变量与公共内存中的变量进行同步。
public class Service { private boolean isContinueRun = true; public void runMethod() { while (isContinueRun) { } System.out.println("stop..."); } public void stopMethod() { isContinueRun = false; } }
public class ThreadA extends Thread { private Service service; public ThreadA(Service service) { this.service = service; } @Override public void run() { service.runMethod(); } } public class ThreadB extends Thread { private Service service; public ThreadB(Service service) { this.service = service; } @Override public void run() { service.stopMethod(); } }
public class Run { public static void main(String[] args) { try { Service service = new Service(); ThreadA a = new ThreadA(service); a.start(); Thread.sleep(1000); ThreadB b = new ThreadB(service); b.start(); System.out.println("已经发起中止命令了"); } catch (Exception e) { e.printStackTrace(); } } }
运行main,控制台打印结果以下:
已经发起中止命令了
出现死循环。
对Service类作以下修改:
public class Service { private boolean isContinueRun = true; public void runMethod() { String anyString = new String(); while (isContinueRun) { synchronized (anyString) { } } System.out.println("stop..."); } public void stopMethod() { isContinueRun = false; } }
此时控制台打印结果以下:
已经发起中止命令了 stop...
关键字synchronized能够保证在同一时刻,只有一个线程能够执行某一个方法或某一个代码块。它包含两个特征:互斥性和可见性。同步synchronized不只能够解决一个线程看到对象处于不一致的状态,还能够保证进入同步方法或者同步代码块的每一个线程都看到由同一个锁保护以前全部的修改结果。