zoukankan      html  css  js  c++  java
  • Java多线程编程核心技术---对象及变量的并发访问(二)

    数据类型String的常量池特性

    在JVM中具有String常量池缓存的功能。

    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对象,但他并不放入缓存中。


    同步synchronized方法无限等待与解决
    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...");
    		}
    	}
    }
    
    

    此时控制台打印结果如下:

    methodA begin...
    methodB begin...
    methodB end...
    

    methodA()和methodB()对不同的对象加锁,所以线程A持有的锁不会对线程B造成影响。


    多线程的死锁

    Java线程死锁是一个经典的多线程问题,因为不同的线程都在等待根本不可能被释放的锁,从而导致所有的任务都无法继续完成。

    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();
    	}
    }
    
    

    控制台打印结果同上。


    内置类与同步-实验1
    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
    

    由于持有不同的对象监视器,所以打印结果是乱序的。

    内置类与同步-实验2
    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是以同步方式执行的。


    volatile关键字
    关键字volatile与死循环

    死循环例子

    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的比较

    1. 关键字volatile是线程同步的轻量级实现,性能比synchronized好。volatile只能修饰变量,synchronized可以修饰方法和代码块。
    2. 多线程访问volatile不会发生阻塞,synchronized会出现阻塞。
    3. volatile能保证数据的可见性,但不能保证原子性。synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步。
    4. 关键字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++操作

    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代码块有volatile同步的功能

    关键字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不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程都看到由同一个锁保护之前所有的修改结果。

  • 相关阅读:
    基于vue2.0 +vuex+ element-ui后台管理系统:包括本地开发调试详细步骤
    require.js实现js模块化编程(二):RequireJS Optimizer
    require.js实现js模块化编程(一)
    树型权限管理插件:jQuery Tree Multiselect详细使用指南
    表格组件神器:bootstrap table详细使用指南
    后台管理系统中的重点知识大全
    Ajax最详细的参数解析和场景应用
    npm常用命令小结
    详解javascript,ES5标准中新增的几种高效Object操作方法
    git入门学习(二):新建分支/上传代码/删除分支
  • 原文地址:https://www.cnblogs.com/umgsai/p/5589945.html
Copyright © 2011-2022 走看看