第一章:线程锁synchronized学习

第一章:线程锁synchronized学习第一章:线程锁synchronized学习

大家好,又见面了,我是你们的朋友全栈君。

package com.xiaoyexinxin.ThreadLearn;

public class MyThread extends Thread{
	private int count=5;
	
	//synchronized加锁
	public synchronized void run(){
		count --;
		System.out.println(this.currentThread().getName()+"count="+count);
	}
	
        public static void main(String args[]{
		MyThread myThread=new MyThread();
		Thread t1=new Thread(myThread,"t1");
		Thread t2=new Thread(myThread,"t2");
		Thread t3=new Thread(myThread,"t3");
		Thread t4=new Thread(myThread,"t4");
		Thread t5=new Thread(myThread,"t5");
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
	}

}

执行结果:

t1count=4
t2count=3
t4count=2
t5count=1
t3count=0

每次的运行的结果走的线程顺序都不一样,说明是通过cpu分配的先后顺序。

 

下面如果我们把synchronized去掉,就会出现线程不安全的情况,多次执行程序会出现下面的结果:   

t1count=3
t5count=1
t4count=2
t2count=3
t3count=0

我们分析下为什么会出现这种情况:因为没有加锁,从结果可以看出,可能是t1线程先抢到执行资源,执行run方法,count减一,count为4,然后在他还没执行system输出语句时,资源被另外一个线程夺取了,这个线程开始执行run方法,执行了count减一,此时count变为3了。然后先前那个资源这时又重新夺取了资源,执行system输出语句,可是它此时再去取count值时,取到的是3,已经不是它应该得到4了。现实中,我们想到的的数据结果是43210都有,可是由于线程不安全的影响会出现数据缺失。所以这就是所谓的线程不安全。

另外可以看出执行结果的顺序比较混乱,说明先夺取资源执行方法体不一定先输出,先输出也不一定先打印到控制台。

下面我们分析加锁的影响:当多个线程访问myThread的run方法是,以排队的方式进行处理(这里排队是按照CPU分配的先后顺序而定的),一个线程想要执行synchronized修饰的方法里的代码,首先是尝试获得锁,如果得到锁,执行synchronized代码体内容,直到整个方法执行完才会释放锁让别的线程进来,这样就不会出现上面数据缺失的现象,拿不到锁,这个线程就会不断的尝试获得这把锁,直到拿到为止,而且是多个线程同时去竞争这把锁。(也就是会有锁竞争的问题,锁竞争问题对于线程比较少的情况倒是影响不大,但是如果线程很多的话,同一时间有很多线程要抢夺一把锁,CPU会瞬间达到很高的占用率,很有可能就宕机了,其实这和我们双十一抢购商品是一样的,在0点0分0秒瞬间有几十万几百万的请求过来,这对于一般的服务器来说,瞬间就崩溃了,因此我们尽量避免 锁竞争的问题)

 

示例二:多个线程多个锁

        多个线程多个锁:多个线程,每个线程都可以拿到自己指定的锁,分别获得锁之后,执行synchronized方法体的内容。

package com.xiaoyexinxin.ThreadLearn;

public class MultiThread2 {

	private int num=0;
	public synchronized void printNum(String tar){
		if(tar.equals("a")){
			num=100;
			System.out.println("线程a");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}else{
			num=200;
			System.out.println("线程b");

		}
		System.out.println("输出的线程是:"+tar+",num:"+num);
	}
	
	public static void main(String[] args) {
		final MultiThread2 mThread1=new MultiThread2();
		final MultiThread2 mThread2=new MultiThread2();//设为final表示此对象不能别继承,没有子类。如果是方法被定义成final,则此方法不能被重写
		Thread t1=new Thread(new Runnable() {
			
			public void run() {
				// TODO Auto-generated method stub
				mThread1.printNum("a");
			}
		});
		Thread t2=new Thread(new Runnable() {
			
			public void run() {
				// TODO Auto-generated method stub
				mThread2.printNum("b");
				
			}
		});
		t1.start();
		t2.start();

	}

}

 

执行结果:

 

线程a
线程b
输出的线程是:b,num:200
输出的线程是:a,num:100

 

    我们运行该类,会看到如下结果,可以看到线程t1和t2互不影响,各自运行各自的,我们让t1线程休息了1秒钟,这样t2线程执行完了,t1线程才执行完。synchronized取得的锁都是对象锁,而不是把一段代码(方法)当做锁,所以上面代码中哪个线程先执行synchronized关键字的方法,那个线程就持有该方法所属对象的锁(Lock),两个对象,线程获得的就是两个不同的锁,他们互不影响。

 

 有一种情况则是相同的锁,那就是在静态方法上加synchronized关键字,表示锁定.class类,类一级别的锁(独占.class类)。为了看效果,我们在变量num前面加static关键字, 给printNum方法加上static关键字,如下图所示。

结果:

线程a
输出的线程是:a,num:100
线程b
输出的线程是:b,num:200

这样情况就跟单个线程单个锁执行的效果一样了。要等先进来的线程把run方法执行完了才能执行另外的线程。

 

 

下面我们聊聊对象锁的同步和异步

同步:synchronized

         同步的概念就是共享,我们要牢牢记住”共享”这两个字,如果不是共享的资源,就没有必要进行同步(举个简单例子,我们存款和取款,一定要同步,因为不同步的 话,资金就乱了,这是不能忍受的)。

异步:asynchronized

         异步的概念就是独立,相互之间不受到任何制约。就好像我们学习http的时候,在页面发起的Ajax请求,我们还可以继续浏览或操作页面的内容,二者之间没有任何关系。

         同步的目的就是为了线程安全,其实对于线程安全来说,需要满足两个特性:原子性(同步)和可见性

        异步的目的及时快了,能同时干多件事。

异步的例子:

package com.xiaoyexinxin.ThreadLearn;

public class MultiThreadAsyn {
	
	public  synchronized void method1(){
		System.out.println(Thread.currentThread().getName());
		try {
			Thread.sleep(3000);
			System.out.println("method1结束");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	 public void method2(){  
	        System.out.println(Thread.currentThread().getName());  
	    }  

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		final MultiThreadAsyn mta=new MultiThreadAsyn();
		Thread t1=new Thread(new Runnable() {
			
			public void run() {
				// TODO Auto-generated method stub
				mta.method1();
			}
		});
		
		Thread t2=new Thread(new Runnable() {
			
			public void run() {
				// TODO Auto-generated method stub
				mta.method2();
			}
		});
		
		t1.start();
		t2.start();

	}

}

执行结果是:

Thread-0
Thread-1
method1结束

运行上面的main方法,我们会看到t1和t2同时打印出来了,这说明此时method1和method2是异步执行的。

 

下面我们给method2方法也加上synchronized,看看执行结果

 

Thread-0
method1结束
Thread-1

再运行main方法,这回我们看到t1信息输出4秒后才会看到t2信息,出现这种情况的原因是,我们只new了一个对象,而synchronized关键字锁的便是对象,由于method1和method2现在都被synchronized关键字修饰,因此只要是访问该对象的这两个方法的线程都会进行同步操作,也就是说谁先拿到对象的锁谁便先执行,执行完之后,释放锁,这时下一个线程才能执行。

         A线程先持有object对象的Lock锁,B线程如果在这个时候调用对象中的同步(synchronized)方法则需要等待,也就是同步。

        A线程先持有object对象的Lock锁,B线程可以以异步的方式调用对象中的非synchronized修饰的方法。

 

4,使用继承Thread实例:

package client.cfca;
/**
 * 
 * @author liuxin
 * @date   2018年8月1日
 */
public class ad {

	public static void main(String[] args) {
		Dog t1=new Dog();
		Dog t2=new Dog();
		Dog t3=new Dog();
		Dog t4=new Dog();
		t1.setName("t1");
		t2.setName("t2");
		t3.setName("t3");
		t4.setName("t4");
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

class Dog extends Thread{
	private int count=3;
	public  void run(){
			try {
				synchronized (this) {
					for(int i=0;i<3;i++){
						System.out.println(Thread.currentThread().getName()+"    count="+count--);
						sleep(5000);
					}
					System.out.println(Thread.currentThread().getName());
				}
				
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	}
}

结果:每隔5s打印4条记录

t1    count=3
t3    count=3
t4    count=3
t2    count=3
t1    count=2
t3    count=2
t4    count=2
t2    count=2
t1    count=1
t3    count=1
t4    count=1
t2    count=1
t1
t3
t4
t2

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/106151.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Navicat激活码在线生成(破解版激活)

    Navicat激活码在线生成(破解版激活),https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

  • oracle 用户赋权_oracle数据库创建只读用户

    oracle 用户赋权_oracle数据库创建只读用户ORACLE创建用户赋予权限删除用户oracle数据库的权限系统分为系统权限与对象权限。一.ORACLE默认管理员密码二.创建用户及密码。三.赋予权限。oracle数据库的权限系统分为系统权限与对象权限。系统权限(databasesystemprivilege)可以让用户执行特定的命令集。例如,createtable权限允许用户创建表,grantanyprivilege权限允许用户授予任何系统权限。对象权限(databaseobjectprivilege)可以让用户能

    2022年10月29日
  • 基于epoll的TP传输层实现

    1.抽象TP传输层设计在使用epoll实现实际的传输层之前,先设计一个抽象的传输层,这个抽象的传输层是传输层实现的接口层。接口层中一共有以下几个通用的类或者接口:(1)Socket:通用的套接

    2021年12月28日
  • 多级时间轮定时器_时间轮与哈希表定时

    多级时间轮定时器_时间轮与哈希表定时时间轮简述顾名思义,时间轮就像一个轮子,在转动的时候外界会指向轮子不同的区域,该区域就可以被使用。因此只要将不同时间的定时器按照一定的方法散列到时间轮的不同槽(即时间轮划分的区域)之中,就可以实现在运转到某个槽时,进行判断该定时器是否已经到达运行时间(需要判断是由于有的定时器并非在这一圈就需要运行,可能需要后面几圈才会运行。从图中也可以看出,每个槽中的定时器是以(双向)链表…

  • 数字信号处理matlab实验心得,数字信号处理学习心得体会3篇

    《数字信号处理》是我们通信工程和电子类专业的一门重要的专业基础课程,主要任务是研究数字信号处理理论的基本概念和基本分析方法,通过建立数学模型和适当的数学分析处理,来展示这些理论和方法的实际应用。数字信号处理技术正飞速发展,它不但自成一门学科,更是以不同形式影响和渗透到其他学科。以下是小编为大家精心准备的:,欢迎参考阅读!数字信号处理学习心得体会一随机数字信号处理是由多种学科知识交叉渗透形成的,在通…

  • 时序数据库详解和使用说明_时序数据库 应用场景

    时序数据库详解和使用说明_时序数据库 应用场景时序数据往往是由百万级甚至千万级终端设备产生的,写入并发量比较高,属于海量数据场景。

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号