多线程的调用可以使得程序运行的效率大幅提升,但线程的使用一方面会降低可读性,一方面给代码的运行带来随机性。针对随机性问题,synchronized修饰符可以使得某方法同时只能被一个线程调用,而join方法会让主线程等待当前子线程执行结束再继续执行。它们都能在一定程度上控制多线程程序的执行顺序。
直接用线程输出 Caller线程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Caller implements Runnable{ String msg; Callme target; Thread t; public Caller(Callme targ, String s) { target = targ; msg = s; t = new Thread(this); t.start(); } // 每个线程都会调用的方法 public void run() { target.call(msg); } }
Callme类 1 2 3 4 5 6 7 8 9 10 11 12 13 public class Callme { // 该方法可同时被多个线程调用 void call(String msg) { System.out.print("["+msg); try { Thread.sleep(1000); // 暂停1s } catch (InterruptedException e) { System.out.println("Interrupted"); } System.out.println("]"); } }
Sync主类 1 2 3 4 5 6 7 8 public class Sync { public static void main(String args[]) { Callme target = new Callme(); // 被三个线程共享的资源 Caller ob1 = new Caller(target,"Hello"); Caller ob2 = new Caller(target, "Synchronized"); Caller ob3 = new Caller(target,"World"); } }
可能出现的结果 1 2 3 [Hello[Synchronized[World] ] ]
由于线程的随机性,Hello,Synchronized,World的顺序是随机的
synchronized 修饰方法 Callme类 用synchronized修饰call方法
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Callme { // 该方法同时只可以被一个线程调用 synchronized void call(String msg) { System.out.print("["+msg); try { Thread.sleep(1000); // 暂停1s } catch (InterruptedException e) { System.out.println("Interrupted"); } System.out.println("]"); } }
可能的输出结果 1 2 3 [Hello] [World] [Synchronized]
同理,由于线程的随机性,Hello,Synchronized,World的顺序是随机的
join方法 先去掉Callme类中call方法的synchronized修饰符进行讨论。 join()方法使得,主线程 等待这个线程运行结束,再运行主线程(其他线程不受影响)
Sync类 1 2 3 4 5 6 7 8 9 10 11 public class Sync { public static void main(String args[]) throws InterruptedException{ Callme target=new Callme(); // 被三个线程共享的资源 Caller ob1=new Caller(target,"Hello"); ob1.t.join(); Caller ob2=new Caller(target,"Synchronized"); ob2.t.join(); Caller ob3=new Caller(target,"World"); ob3.t.join(); }
必然会出现的结果 1 2 3 [Hello] [Synchronized] [World]
synchronized 修饰代码块 synchronized除了可以用来修饰方法外,还可以用来修饰代码块,格式如下:
1 2 3 synchronized(object) { // statements to be synchronized }
同样,Callme类的call方法不用synchronized修饰,而将Caller线程的run方法改成:
1 2 3 4 5 public void run() { synchronized (target) { target.call(msg); } }
输出的结果和synchronized方法是一样的,因为main()中的target是三个线程共享的对象,因此对象只能同时被一个线程调用,如果放入sychronized的参数为字符串msg,或者将Sync改成:
1 2 3 4 5 6 7 8 9 10 public class Sync { public static void main(String args[]) { Callme target = new Callme(); Callme target2 = new Callme(); Callme target3 = new Callme(); Caller ob1 = new Caller(target,"Hello"); Caller ob2 = new Caller(target2, "Synchronized"); Caller ob3 = new Caller(target3,"World"); } }
这样是起不了同步阻塞的效果的,即与直接输出无异。