JAVA多线程入门

继承Thread父类 线程代码执行顺序和调用顺序无关,例如:

public class MyThread extends Thread {@Override public void run(){ super.run(); System.out.println("MyThread"); } /**运行顺序存疑 * 并没有发现随机性 */ public static void main(String[] args) { MyThread thread = new MyThread(); thread.run(); System.out.println("mainThread"); }}

上述代码执行理论上“MyThread”和“mainThread”打印顺序是随机的,和调用顺序无关,实际情况存疑。
线程执行具有随机性,CPU的执行具有不确定性
public class MyThread1 extends Thread {@Override public void run(){ try { for (int i = 0; i < 10; i++){ int time = (int) (Math.random()*1000); Thread.sleep(time); System.out.println("run:"+Thread.currentThread().getName()); } }catch (InterruptedException e) { e.printStackTrace(); } }public static void main(String[] args) throws InterruptedException { MyThread1 thread = new MyThread1(); thread.setName("myThread"); thread.start(); for (int i = 0; i<10; i++){ int time = (int) (Math.random()*1000); Thread.sleep(time); System.out.println("main:"+Thread.currentThread().getName()); }} }/*结果: run:myThread run:myThread run:myThread main:main main:main run:myThread run:myThread run:myThread main:main main:main main:main main:main run:myThread run:myThread main:main run:myThread run:myThread main:main main:main main:main */

start方法并不代表线程启动,线程启动顺序由CPU执行顺序决定,无序性。
public class MyThread2 extends Thread { private int i; public MyThread2(int i){ super(); this.i = i; } @Override public void run(){ System.out.println("myThread:"+i); }public static void main(String[] args){ MyThread2 t1 = new MyThread2(1); MyThread2 t2 = new MyThread2(2); MyThread2 t3 = new MyThread2(3); MyThread2 t4 = new MyThread2(4); MyThread2 t5 = new MyThread2(5); MyThread2 t6 = new MyThread2(6); MyThread2 t7 = new MyThread2(7); MyThread2 t8 = new MyThread2(8); MyThread2 t9 = new MyThread2(9); MyThread2 t10 = new MyThread2(10); MyThread2 t11 = new MyThread2(11); MyThread2 t12 = new MyThread2(12); MyThread2 t13 = new MyThread2(13); MyThread2 t14 = new MyThread2(14); MyThread2 t15 = new MyThread2(15); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); t6.start(); t7.start(); t8.start(); t9.start(); t10.start(); t11.start(); t12.start(); t13.start(); t14.start(); t15.start(); } }/*结果: myThread:2 myThread:1 myThread:3 myThread:4 myThread:7 myThread:8 myThread:11 myThread:12 myThread:15 myThread:13 myThread:14 myThread:5 myThread:6 myThread:9 myThread:10 */

Runnable接口构造线程 java是单基础,继承Thread类有局限性,所以更多的是使用Runnable接口去新建线程,Thread类有构造方法使用Runnable接口新建线程。
public class RunableTest implements Runnable {@Override public void run() { System.out.println("Runable线程运行中:"+Thread.currentThread().getName()); }public static void main(String[] arg){ RunableTest runableTest = new RunableTest(); Thread thread = new Thread(runableTest); thread.start(); System.out.println("mainThread:"+Thread.currentThread().getName() ); } }

实例变量与线程安全 实例变量不共享 线程间变量不共享,数据不共享情况:
public class ShareThread extends Thread{ private int count = 5; public ShareThread(String name){ super(); this.setName(name); } @Override public void run(){ super.run(); while (count >0){ count--; System.out.println("由"+Thread.currentThread().getName()+"计算,count="+count); } }public static void main(String[] args){ ShareThread shareThread1 = new ShareThread("A"); ShareThread shareThread2 = new ShareThread("B"); ShareThread shareThread3 = new ShareThread("C"); shareThread1.start(); shareThread2.start(); shareThread3.start(); } }/* 由A计算,count=4 由B计算,count=4 由B计算,count=3 由B计算,count=2 由B计算,count=1 由B计算,count=0 由A计算,count=3 由A计算,count=2 由A计算,count=1 由A计算,count=0 由C计算,count=4 由C计算,count=3 由C计算,count=2 由C计算,count=1 由C计算,count=0 */

线程数据共享 共享数据情况就是多个线程可以访问同一个变量。
public class ShareThread1 extends Thread { private int count= 10; @Override synchronized public void run(){ super.run(); count--; //不要使用for语句,因为使用同步后线程就没有运行机会了 //一直由线程进行减法运算 System.out.println("由"+Thread.currentThread().getName()+"计算,count="+count); }public static void main(String[] args){ ShareThread1 thread1 = new ShareThread1(); Thread a = new Thread(thread1,"A"); Thread b = new Thread(thread1,"B"); Thread c = new Thread(thread1,"C"); Thread d = new Thread(thread1,"D"); Thread e = new Thread(thread1,"E"); Thread f = new Thread(thread1,"F"); a.start(); b.start(); c.start(); d.start(); e.start(); f.start(); }}/*结果: 由A计算,count=9 由D计算,count=8 由E计算,count=7 由F计算,count=6 由C计算,count=5 由B计算,count=4 */

synchronized关键字表示执行多个线程时以排队的方式进行处理。线程执行时会上锁,执行完毕后会解锁,线程调用run()方法前会请求线程锁,若已经上锁,则会不断请求线程锁。
System.out.println()使用时可能会发生“非线程安全”问题,里面打印i--时,会先执行i--,然后打印结果,造成线程安全问题。
public class ShareThread2 extends Thread { private int i = 5; @Override public void run(){ System.out.println("i="+ (i--) +",threadName="+Thread.currentThread().getName()); //i--在println之前执行,故可能发生非线程安全问题 }public static void main(String[] args) { ShareThread2 run = new ShareThread2(); Thread t1 = new Thread(run); Thread t2 = new Thread(run); Thread t3 = new Thread(run); Thread t4 = new Thread(run); Thread t5 = new Thread(run); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }/* i=4,threadName=Thread-1 i=5,threadName=Thread-3 i=2,threadName=Thread-5 i=5,threadName=Thread-4 i=3,threadName=Thread-2*/

常用函数 currentThread()方法
currentThread返回代码段被哪个线程调用的信息。
public class CountOpertrate extends Thread { public CountOpertrate(){ System.out.println("CountOpertate-build-start"); System.out.println("Thread.currentThread().getName() = "+Thread.currentThread().getName()); System.out.println("this.getName()"+this.getName()); System.out.println("CountOpertrate-build-end"); }@Override public void run(){ System.out.println("run-start"); System.out.println("Thread.currentThread().getName() = "+Thread.currentThread().getName()); System.out.println("this.getName()"+this.getName()); System.out.println("run-end"); }public static void main(String[] args) { CountOpertrate countOpertrate = new CountOpertrate(); Thread t = new Thread(countOpertrate); t.setName("TEST"); t.start(); }/*result: CountOpertate-build-start Thread.currentThread().getName() = main this.getName()Thread-0 CountOpertrate-build-end run-start Thread.currentThread().getName() = TEST this.getName()Thread-0 run-end */

【JAVA多线程入门】上述代码显示,Count构建时时用的main线程,run是跑在TEST线程上。
isAlive()方法
isAlive方法是判断当前线程是处于活动状态。
public class IsAliveTest extends Thread { @Override public void run() { System.out.println("run = "+this.isAlive()); }public static void main(String[] args) throws InterruptedException { IsAliveTest i = new IsAliveTest(); System.out.println("start =="+i.isAlive()); i.start(); Thread.sleep(1000); System.out.println("end =="+i.isAlive()); } }/*result: start ==false run = true end ==false */

若将线程对象以构造参数传递给Thread对象进行start,结果会有差异。
public class IsAliveTest1 extends Thread {public IsAliveTest1(){ System.out.println("IsAliveTest1-Start"); System.out.println("Thread.currentThread().getName() = "+Thread.currentThread().getName()); System.out.println("Thread.currentThread().isAlive() = "+Thread.currentThread().isAlive()); System.out.println("this.getName() = "+this.getName()); System.out.println("this.isAlive() = "+this.isAlive()); System.out.println("IsAliveTest1-end"); } @Override public void run(){ System.out.println("run-Start"); System.out.println("Thread.currentThread().getName() = "+Thread.currentThread().getName()); System.out.println("Thread.currentThread().isAlive() = "+Thread.currentThread().isAlive()); System.out.println("this.getName() = "+this.getName()); System.out.println("this.isAlive() = "+this.isAlive()); System.out.println("run-end"); }public static void main(String[] args) throws InterruptedException { IsAliveTest1 test1 = new IsAliveTest1(); Thread t1 = new Thread(test1); System.out.println("main bigin t1 isAlive = "+t1.isAlive()); t1.setName("AAA"); t1.start(); Thread.sleep(1000); System.out.println("main end t1 isAlive = "+t1.isAlive()); } }/*result: IsAliveTest1-Start Thread.currentThread().getName() = main Thread.currentThread().isAlive() = true this.getName() = Thread-0 this.isAlive() = false IsAliveTest1-end main bigin t1 isAlive = false run-Start Thread.currentThread().getName() = AAA Thread.currentThread().isAlive() = true this.getName() = Thread-0 this.isAlive() = false run-end main end t1 isAlive = false */

sleep()方法
sleep()方法是在括号中毫秒内使正在执行的线程暂停执行的方法,正在执行的线程是this.currentThread()返回的线程。
public class sleepTest extends Thread { @Override public void run() { try { System.out.println("run threadName = "+this.getName()+"-begin"); Thread.sleep(2000); System.out.println("run ThreadName = "+this.getName()+"-end"); } catch (InterruptedException e) { e.printStackTrace(); } }public static void main(String[] args) { sleepTest test = new sleepTest(); System.out.println("begin = "+ System.currentTimeMillis()); test.run(); //test.start(); System.out.println("end = "+System.currentTimeMillis()); } }/*直接用run()方法 begin = 1521341785159 run threadName = Thread-0-begin run ThreadName = Thread-0-end end = 1521341787160 */ /*使用start()方法 begin = 1521341902632 end = 1521341902632 run threadName = Thread-0-begin run ThreadName = Thread-0-endmain和sleepTest线程是异步的,所以先打印时间 */

停止线程 interrupt()方法
interrupt方法并不是立刻停止线程。而是在当前线程中打一个停止标记。
public class InterruptTest extends Thread {@Override public void run() { super.run(); for (int i = 0; i<50000; i++){ System.out.println("i = "+ (i+1)); } }public static void main(String[] args) { try { InterruptTest test = new InterruptTest(); test.start(); Thread.sleep(2000); Thread.interrupted(); } catch (InterruptedException e) { System.out.println("main-catch"); e.printStackTrace(); } } } /* 无法停止,打印50000条记录 */

判断线程是否是停止状态
interrupted()方法,测试当前线程是否已经是中断状态,执行后将状态标志改为false。
isInterrupted()方法,测试线程对象是否已经为中断状态,但不清除状态标志。
异常法停止线程
可以使用isInterrupted方法判断线程停止标志状态并抛出InterruptedException,使用interrupt()方法停止线程后,因为接收到停止状态码,抛出异常进入catch分支,继而终止线程。
public class StopThreadTest extends Thread {@Override public void run() { super.run(); try { for (int i=0; i<1000000; i++){ if (this.isInterrupted()){ System.out.println("已是停止状态,线程退出!"); throw new InterruptedException(); } System.out.println("i = "+(i+1)); } System.out.println("for下面的"); } catch (InterruptedException e) { System.out.println("线程run()方法catch!线程异常终止"); e.printStackTrace(); }}public static void main(String[] args) {try { StopThreadTest test = new StopThreadTest(); test.start(); Thread.sleep(1000); test.interrupt(); } catch (InterruptedException e) { System.out.println("maincatch"); e.printStackTrace(); } System.out.println("end!"); }}/*result: ... ... i = 274280 i = 274281 i = 274282 i = 274283 i = 274284 end! 已是停止状态,线程退出! 线程run()方法catch!线程异常终止 java.lang.InterruptedException at com.tz.StopThread.StopThreadTest.run(StopThreadTest.java:15)*/

沉睡中停止进程
线程在sleep状态下停止,会直接报异常,并进入catch退出,有两种情况,一个是先sleep再interrupt,还有就是先interrupt再停止。
//先sleeppublic class StopSleep1 extends Thread{@Override public void run() { super.run(); try { System.out.println("run-begin"); Thread.sleep(200000); System.out.println("run-end"); } catch (InterruptedException e) { System.out.println("在沉睡中停止,run()进入catch"+this.isInterrupted()); e.printStackTrace(); } }public static void main(String[] args) { try { StopSleep1 sleep1 = new StopSleep1(); sleep1.start(); Thread.sleep(200); sleep1.interrupt(); } catch (InterruptedException e) { System.out.println("main-catch"); e.printStackTrace(); } System.out.println("end!"); } }/*result: run-begin end! 在沉睡中停止,run()进入catchfalse java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.tz.StopThread.StopSleep1.run(StopSleep1.java:13) */

//后sleeppublic class StopSleep2 extends Thread {@Override public void run() { super.run(); try { for (int i = 0; i<100000; i++){ System.out.println("i = "+(i+1)); } System.out.println("run-begin"); Thread.sleep(200000); System.out.println("run-end"); } catch (InterruptedException e) { System.out.println("先停止再遇到sleep,run()进入catch"+this.isInterrupted()); e.printStackTrace(); } }public static void main(String[] args) {StopSleep2 sleep2 = new StopSleep2(); sleep2.start(); sleep2.interrupt(); System.out.println("end!"); }}/*result: i = 99997 i = 99998 i = 99999 i = 100000 run-begin 先停止再遇到sleep,run()进入catchfalse java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.tz.StopThread.StopSleep2.run(StopSleep2.java:16) */

暴力停止线程
使用stop方法停止线程,这个方法很暴力。
public class StopThread extends Thread {private int i = 0; @Override public void run() { try { while (true){ i++; System.out.println("i=" +i); Thread.sleep(1000); }} catch (InterruptedException e) { e.printStackTrace(); } }public static void main(String[] args) { try { StopThread thread = new StopThread(); thread.start(); Thread.sleep(8000); thread.stop(); System.out.println("stop暴力停止"); } catch (InterruptedException e) { e.printStackTrace(); } } }

stop方法已经作废,尽量不使用!!!
stop方法释放锁,会造成数据不一致的结果。
public class StopThread1 extends Thread {private SynchronizedObject object; public StopThread1(SynchronizedObject object){ super(); this.object = object; }@Override public void run() { object.printString("b","bb"); }public static void main(String[] args) {try { SynchronizedObject object = new SynchronizedObject(); StopThread1 thread1 = new StopThread1(object); thread1.start(); Thread.sleep(500); thread1.stop(); System.out.println("object.getUsername()="+object.getUsername()); System.out.println("object.getPassword()="+object.getPassword()); } catch (InterruptedException e) { e.printStackTrace(); }} }/*result: object.getUsername()=b object.getPassword()=aa */

return 停止线程
可以将interrupt()方法与return结合实现停止线程。
public class ReturnStopThread extends Thread{@Override public void run() { while(true){ if (this.isInterrupted()){ System.out.println("停止!"); return; } System.out.println("timer = "+System.currentTimeMillis()); } }public static void main(String[] args) throws InterruptedException { ReturnStopThread thread = new ReturnStopThread(); thread.start(); Thread.sleep(2000); thread.interrupt(); } }/*result: ... ... timer = 1521358261861 timer = 1521358261861 timer = 1521358261861 timer = 1521358261861 timer = 1521358261861 timer = 1521358261861 timer = 1521358261861 停止! */

建议还是使用抛异常来停止进程,因为抛异常可以通过catch语句将线程停止事件上抛,是线程停止事件得以传播。
暂停线程 暂停线程意味着次现场可以恢复运行,在Java多线程中可以使用suspend()方法暂停线程,使用resume()方法恢复线程的执行。
public class SuspendTestThread extends Thread { private long i = 0; public long getI(){ return i; }public void setI(long i) { this.i = i; }@Override public void run() { while(true){ i++; } }public static void main(String[] args) { try { SuspendTestThread thread = new SuspendTestThread(); thread.start(); Thread.sleep(1000); //A段 thread.suspend(); System.out.println("线程暂停!"); System.out.println("A= " +System.currentTimeMillis()+" i="+thread.getI()); Thread.sleep(1000); System.out.println("A= " +System.currentTimeMillis()+" i="+thread.getI()); //B段 thread.resume(); Thread.sleep(1000); System.out.println("线程唤醒!"); System.out.println("B= " +System.currentTimeMillis()+" i="+thread.getI()); Thread.sleep(1000); System.out.println("B= " +System.currentTimeMillis()+" i="+thread.getI()); //c段 thread.suspend(); System.out.println("线程又暂停"); System.out.println("C= " +System.currentTimeMillis()+" i="+thread.getI()); Thread.sleep(1000); System.out.println("C= " +System.currentTimeMillis()+" i="+thread.getI()); } catch (InterruptedException e) { e.printStackTrace(); } } } /*result: 线程暂停! A= 1521516350803 i=620113660 A= 1521516351803 i=620113660 线程唤醒! B= 1521516352804 i=1259895883 B= 1521516353804 i=1901121177 线程又暂停 C= 1521516353804 i=1901176584 C= 1521516354804 i=1901176584*/

明显线程在A和C段暂停执行了,在B段唤醒之后又能重新执行。
suspend和rusume的缺点
  1. 独占
使用线程暂停时,如果使用不当,容易造成对公共的同步对象的独占,导致其他线程无法访问公共同步对象。
//model.class public class SynchronizedObject { synchronized public void printString(){ System.out.println("begin"); if (Thread.currentThread().getName().equals("a")){ System.out.println("a线程永久陷入沉睡!"); Thread.currentThread().suspend(); } System.out.println("end"); } }

public class SuspendTestThread1 extends Thread { public static void main(String[] args) { try { final SynchronizedObject object = new SynchronizedObject(); Thread thread1 = new Thread(){ @Override public void run() { object.printString(); } }; thread1.setName("a"); thread1.start(); Thread.sleep(1000); Thread thread2 = new Thread(){ @Override public void run() { System.out.println("thraed2启动,但进入不了printString()方法"); System.out.println("因为printString()方法被a线程锁定并独占了"); object.printString(); } }; thread2.start(); } catch (InterruptedException e) { e.printStackTrace(); } } } /*result: begin a线程永久陷入沉睡! thraed2启动,但进入不了printString()方法 因为printString()方法被a线程锁定并独占了 */

  1. 不同步
    因为线程暂停可能会导致数据不同步的情况。
public class MyObject { private String username = "l"; private String password = "ll"; public void setValue(String username,String password){ this.username = username; if (Thread.currentThread().getName().equals("a")){ System.out.println("停止a线程!"); Thread.currentThread().suspend(); } this.password = password; } public void printUsernamePassword(){ System.out.println(username+""+password); } }public class SuspendTestThread2 extends Thread { public static void main(String[] args) throws InterruptedException { final MyObject myObject = new MyObject(); Thread thread1 = new Thread(){ @Override public void run() { myObject.setValue("a","aa"); } }; thread1.setName("a"); thread1.start(); Thread.sleep(500); Thread thread2 = new Thread(){ @Override public void run(){ myObject.printUsernamePassword(); } }; thread2.start(); } }/*result: 停止a线程! all */

suspend()和resume()方法已经废弃,不建议使用,可以研究。
yield()方法 yield()方法是让当前线程放弃cpu资源,但放弃的时间不确定,可能刚刚放弃就立刻获得cpu资源。
public class YieldTestThread extends Thread { @Override public void run() { long beginTime = System.currentTimeMillis(); int count = 0; for (int i = 0; i < 50000000; i++){ //Thread.yield(); count = count + (i+1); } long endTime = System.currentTimeMillis(); System.out.println("用时:"+(endTime-beginTime)+"毫秒"); }public static void main(String[] args) { YieldTestThread thread = new YieldTestThread(); thread.start(); } }/*result1:(不加yield) 用时:18毫秒 *//*result2:(加yield) 用时:3362毫秒 */

线程优先级 线程可以划分优先级,从1-10级,其他会报错。
线程的优先级具有承继性。
优先级规则,总是大部分先执行优先级高的线程。
//线程1 public class PriorityTestThread extends Thread{ @Override public void run() { long beginTime = System.currentTimeMillis(); long addResult = 0; for (int j = 0; j < 10; j++){ for(int i = 0; i<50000; i++){ Random random = new Random(); random.nextInt(); addResult = addResult+1; } } long endTime = System.currentTimeMillis(); System.out.println("* * * * * * thread 1 use time="+(endTime - beginTime)); } }//线程2 public class PriorityTestThread1 extends Thread {@Override public void run() { long beginTime = System.currentTimeMillis(); long addResult = 0; for (int j = 0; j < 10; j++){ for(int i = 0; i<50000; i++){ Random random = new Random(); random.nextInt(); addResult = addResult+1; } } long endTime = System.currentTimeMillis(); System.out.println("* * * * * * thread 2 use time="+(endTime - beginTime)); } }public class Run { public static void main(String[] args) { for (int i = 0; i < 100; i++){ PriorityTestThread thread1 = new PriorityTestThread(); thread1.setPriority(10); thread1.start(); PriorityTestThread1 thread2 = new PriorityTestThread1(); thread2.setPriority(1); thread2.start(); } }}/*result: ... ... * * * * * * thread 1 use time=6197 * * * * * * thread 1 use time=6207 * * * * * * thread 1 use time=6252 * * * * * * thread 1 use time=6270 * * * * * * thread 2 use time=6870 * * * * * * thread 2 use time=6524 * * * * * * thread 2 use time=7036 * * * * * * thread 1 use time=7522 * * * * * * thread 1 use time=6448 * * * * * * thread 2 use time=7035 * * * * * * thread 2 use time=7223 * * * * * * thread 2 use time=7025 * * * * * * thread 1 use time=7776 * * * * * * thread 1 use time=6747 * * * * * * thread 2 use time=7261 * * * * * * thread 1 use time=7939 ... ... */

优先级高的不是一定先执行。
守护线程 守护线程是一种特殊线程,特性有“陪伴”的含义,当进程中不存在非守护进程时,守护进程就自动销毁了。典型的守护进程就是垃圾回收线程(垃圾回收器 GC)
public class DaemonTestThread extends Thread { private int i = 0; @Override public void run() { try { while (true){ i++; System.out.println("i = "+ i); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } }public static void main(String[] args) { try { DaemonTestThread thread = new DaemonTestThread(); thread.setDaemon(true); thread.start(); Thread.sleep(5000); System.out.println("我离开Thread对象也不再打印了,也就是停止了!"); } catch (InterruptedException e) { e.printStackTrace(); } }} //线程thread为主线程的守护进程,主线程停止守护进程也结束。/*result: i = 1 i = 2 i = 3 i = 4 i = 5 我离开Thread对象也不再打印了,也就是停止了! */

对象及变量的并发访问 synchronzed同步方法 "非线程安全"会在多个线程对同一个对象中的实例变量进行并发访问时发生产生"脏读",也就是取到的数据其实是被更改过的。而线程安全就是以获得的实例变量的值是经过同步处理的,不会出现脏读现象。
方法内数据为线程安全
"非线程安全"问题存在于"实例变量"中,如果是方法内部私有变量则不存在"非线程安全问题"。
public class HasSelfPrivateNum { public void addI(String username){ try { int num = 0; if (username.equals("a")){ num = 100; System.out.println("a set over!"); Thread.sleep(2000); }else { num = 200; System.out.println("b set over!"); Thread.sleep(2000); } System.out.println(username + " num = "+num); } catch (InterruptedException e) { e.printStackTrace(); } } }public class ThreadA extends Thread { private HasSelfPrivateNum numRef; public ThreadA(HasSelfPrivateNum numRef){ super(); this.numRef = numRef; }@Override public void run() { super.run(); numRef.addI("a"); } }public class ThreadB extends Thread { private HasSelfPrivateNum numRef; public ThreadB(HasSelfPrivateNum numRef){ super(); this.numRef = numRef; }@Override public void run() { super.run(); numRef.addI("b"); }}public class Run { public static void main(String[] args) { HasSelfPrivateNum numRef = new HasSelfPrivateNum(); ThreadA threadA = new ThreadA(numRef); threadA.start(); ThreadB threadB = new ThreadB(numRef); threadB.start(); } }/*result: a set over! b set over! a num = 100 b num = 200*/

实例变量非线程安全
若多个线程访问一个对象实例中的实例变量。则可能发生“非线程安全”问题。
用线程访问的对象中如果有多个实例变量,则运行的结果有可能出现交叉的情况。
如果对象仅有一个实例变量,则有可能出现覆盖的情况。
public class HasSelfPrivateNum{ private int num = 0; //addI()方法前加上synchronized关键字,避免“非线程安全问题” synchronized public void addI(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!"); Thread.sleep(2000); } System.out.println(username + " num = "+num); } catch (InterruptedException e) { e.printStackTrace(); } } }public class ThreadA extends Thread { private HasSelfPrivateNum numRef; public ThreadA(HasSelfPrivateNum numRf){ super(); this.numRef = numRf; }@Override public void run() { super.run(); numRef.addI("a"); } }public class ThreadB extends Thread { private HasSelfPrivateNum numRef; public ThreadB(HasSelfPrivateNum numRf){ super(); this.numRef = numRf; }@Override public void run() { super.run(); numRef.addI("b"); }}public class Run { public static void main(String[] args) { HasSelfPrivateNum numRef = new HasSelfPrivateNum(); ThreadA threadA = new ThreadA(numRef); threadA.start(); ThreadB threadB = new ThreadB(numRef); threadB.start(); } }/*不加synchronized关键字: a set over! b set over! b num = 200 a num = 200 *//*加synchronized关键字: a set over! a num = 100 b set over! b num = 200 */

多个对象多个锁
synchronized关键字取得的锁都对象锁,哪个线程先执行带有synchronized关键字的方法就先获得对象锁,其他线程只能依次等待执行完成。
public class LockTestObject { synchronized public void methodA(){ try { System.out.println("Begin methodA threadName = "+Thread.currentThread().getName()); Thread.sleep(5000); System.out.println("methodA end! endTime = "+System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } }synchronized public void methodB(){ try { System.out.println("Begin methodB threadName = "+Thread.currentThread().getName()); Thread.sleep(5000); System.out.println("methodB end! endTime = "+System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } }public class LockThreadA extends Thread { private LockTestObject object; public LockThreadA(LockTestObject object){ super(); this.object = object; }@Override public void run() { super.run(); object.methodA(); } }public class LockThreadB extends Thread { private LockTestObject object; public LockThreadB(LockTestObject object){ super(); this.object = object; }@Override public void run() { super.run(); object.methodB(); } }public class Run { public static void main(String[] args) { LockTestObject object = new LockTestObject(); LockThreadA threadA = new LockThreadA(object); threadA.setName("A"); LockThreadB threadB = new LockThreadB(object); threadB.setName("B"); threadA.start(); threadB.start(); } }/*methodA方法不加synchronized关键字 Begin methodB threadName = B Begin methodA threadName = A methodB end! endTime = 1521719664578 methodA end! endTime = 1521719664579 *//*methodA方法加上synchronized关键字 Begin methodA threadName = A methodA end! endTime = 1521719556410 Begin methodB threadName = B methodB end! endTime = 1521719561411 */

脏读
所谓脏读是在对去实例变量时该变量已被其他线程改过,读出数据有误。
public class PublicVar { public String userName = "A"; public String password = "AA"; synchronized public void setValue(String userName,String password){ try { this.userName = userName; Thread.sleep(5000); this.password = password; System.out.println("setValue method thread name = "+Thread.currentThread().getName()+" userName = "+userName+" password = "+password); } catch (InterruptedException e) { e.printStackTrace(); } } public void getValue(){ System.out.println("setValue method thread name = "+Thread.currentThread().getName()+" userName = "+userName+" password = "+password); } }public class DirtyReadTestThread extends Thread{ private PublicVar publicVar; public DirtyReadTestThread(PublicVar publicVar){ super(); this.publicVar = publicVar; }@Override public void run() { super.run(); publicVar.setValue("B","BB"); } }public class Run { public static void main(String[] args) { try { PublicVar publicVar = new PublicVar(); DirtyReadTestThread thread = new DirtyReadTestThread(publicVar); thread.start(); Thread.sleep(200); publicVar.getValue(); } catch (InterruptedException e) { e.printStackTrace(); } } }/*result: setValue method thread name = main userName = B password = AA setValue method thread name = Thread-0 userName = B password = BB */

如上所示,main线程出现了脏读,因为getValue()方法不是同步的,只需在getValue前加上synchronized关键字,即可保持数据同步性。
synchronized public void getValue(){ System.out.println("setValue method thread name = "+Thread.currentThread().getName()+" userName = "+userName+" password = "+password); }/*result: setValue method thread name = Thread-0 userName = B password = BB setValue method thread name = main userName = B password = BB */

当线程调用对象包含的synchronized方法时获取了对象的X锁,但别的线程可以调用该实体非synchronized方法。
synchronized 锁重入
一个线程多次请求synchronized方法锁时,可以重复获得方法所在的对象实体的X锁
锁重入,即可重复获得内部锁。
public class Service { 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"); } }public class LockReentryTestThreadextends Thread { @Override public void run() { Service service = new Service(); service.service1(); } }public class Run { public static void main(String[] args) { LockReentryTestThread thread = new LockReentryTestThread(); thread.start(); } }/*result: service1 service2 service3 */

service类中锁就重入了,三个service方法相互调用。
锁重入也支持在父子类间的锁重用。
出现异常,锁自动释放,其他线程继续调用。
synchronized方法的弊端
导致进程等待时间较长,失去多线程的意义,导致程序响应时间过长。
synchronized同步块可以解决这个问题。
synchronized同步代码块
当两个并发的线程访问同一个对象object中的synchronized(this)同步块时,一段时间内只有一个线程能访问并执行,另一个线程必须等待前一个线程执行完毕这个代码块之后,才能执行这块代码。
使用同步synchronized 代码块时,同一个object的同步代码块使用同一个对象监视器,执行一个同步块时对象中其他同步块会被阻塞。
将任意对象作为对象监视器
锁非this对象的优点是:若在一个类中有很多个synchronized方法,这时虽然能实现同步,但会收到阻塞,影响运行效率;若使用同步代码块锁非this对象,则synchronized(非this)代码块中的程序与同步方法是异步的,不与其他锁this的同步方法争抢this锁。则可提高运行效率。
静态同步synchronized方法与synchronized(class)代码块。
关键字还可以作用在static静态方法上,是对方法所在的类.class持锁,而不是对一个对象上锁。
synchronized代码块也可以对class类上锁,实现同步。synchronized(xxx.class)。
数据类型String的常量池特性
由于JVM中String数据类型的常量池特性 a==b 返回true,所以不使用String对象作为对象监视器(对象锁)。
同步synchronized方法无限等待与解决
synchronized同步方法容易造成死循环,是形成陷入死锁。同步块可以解开这个死锁问题,死锁线程依旧跳不出,但其他线程可获得锁。
多线程的死锁
synchronized嵌套代码块将带来死锁。
进入Cmd 输入 jsp 查找Run的id值 在输入 jstack -l 19560 可查看程序运行死锁情况
内置类与静态内置类
锁对象的改变
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 (InterruptedException 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 threadA = new ThreadA(service); threadA.setName("A"); ThreadB threadB = new ThreadB(service); threadB.setName("B"); threadA.start(); //Thread.sleep(50 ); threadB.start(); } }/*延迟50毫秒,争抢两个锁 Abegin1523325785537 Bbegin1523325785585 Aend1523325787539 Bend1523325787585 *//*不延迟,争抢一个锁 Abegin1523325815403 Aend1523325817403 Bbegin1523325817403 Bend1523325819404 */

只要锁对象不变,即使对象属性改变依旧同步,线程还是争抢一个锁。

    推荐阅读