关于JAVA中Thread和synchronized参数的一点理解

什么时候要用Thread

在java开发过程中,有时候会对执行速度、效率有一定要求,这时候就可以借助线程(Thread)来帮助实现了。

Thread的创建和执行

Thread的创建和执行的方法比较多,可根据需要和习惯自由选择。
如简单的创建、执行:

Runnable runnable = new Runnable(){
    
    
	public void run(){
    
    
	}
};
new Thread(runnable).start();
new Thread() {
    
    
	@Override
	public void run() {
    
    
		
	};
}.start();

或者使用线程池来创建、执行

ExecutorService executorService = Executors.newFixedThreadPool(n);//n为最大线程数,根据情况填写数字
Runnable runnable = new Runnable(){
    
    
	public void run(){
    
    
	}
};
executorService.execute(runnable);//开始执行

Thread带来的问题

因为Thread是通过启动多个子线程来同时并发执行子任务这种方法来达到加快代码执行速度这个目的的,所以如果子任务之间或子任务和主任务之间有部分操作或变量本来是需要同步执行的,这时候就会出现问题了。因不同步执行而导致变量出错,不能达到预期的数值或执行结果。如:数值累加不对。

private static int i=0;
Runnable runnable = new Runnable() {
    
    
	public void run() {
    
    
		System.out.println(Thread.currentThread().getName()+"线程执行开始");
		for(int j=0;j<100;j++) {
    
    
				i++;
		}
		System.out.println(Thread.currentThread().getName()+"线程执行结束。i="+i);
	}
};
executorService.execute(runnable);
executorService.execute(runnable);
executorService.execute(runnable);
executorService.shutdown();//需要执行的子线程都设置好后,设置一个通知--完成任务的子线程关闭自己(没有设置这个,你会发现程序运行完,主线程、进程不会停止)

如上这段代码,子线程一共执行了三次,每个子线程任务都是对i这个变量自增1。如果是同步执行,i的最终值应该是300。
而这边使用了子线程,三个子任务一起对i进行了操作,所以i的最终值变得不可预测,在100左右浮动,如下

pool-1-thread-2线程执行结束。i=81
pool-1-thread-3线程执行结束。i=94
pool-1-thread-1线程执行结束。i=102

使用synchronized让共用的执行过程同步

上面的代码之所以会输出这样的结果,是因为3个子线程任务都是对i这个变量进行操作,即这个变量是共用变量(如果单独抽出则是一个共用方法)。
这时候,可以在使用这个变量前,加上synchronized关键字,表示锁定这部分共用代码,同时用到的话,就需要排队使用。

synchronized (this) {
    
    
	for(int j=0;j<100;j++) {
    
    
		i++;
	}
	System.out.println(Thread.currentThread().getName()+"线程执行结束。i="+i);//这句打印也要在synchronized关键字里哦,否则println打印i的时候,for()已经解锁,可能其它线程使用了,i的值就会有变化了
}

修改后,执行的结果就是预期的值了

pool-1-thread-1线程执行结束。i=100
pool-1-thread-2线程执行结束。i=200
pool-1-thread-3线程执行结束。i=300

注意:因为我们上面执行3次的时候,用的是同一个runnable(对象),所以synchronized的参数可以是this(对象,这里就是new Runnable()这个对象),也可以是.class这种或被final修饰过的对象。如果执行的时候,参数是3个不同的runnable(对象),那么synchronized的参数就必须是final修饰的对象了,否则会锁不住。
总结:
1、每new一个接口或类的实例,其中的synchronized(锁)就会有一个,所以多个对象中的synchronized如果还是用this做参数,则两个锁之间互不干扰的,不形成互斥,即会同时执行。除非将参数换成非this的其它不变对象。
2、synchronized的参数如果在过程中发生变化,就会锁不住代码执行。

猜你喜欢

转载自blog.csdn.net/babdpfi/article/details/114749308
今日推荐