博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java多线程知识点整理(synchronized/volatile)
阅读量:6918 次
发布时间:2019-06-27

本文共 6375 字,大约阅读时间需要 21 分钟。

hot3.png

一、线程API知识点

1.sleep()方法

    作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行),这个正在执行的线程是指this.currentThread()返回的线程。

代码:

Thread.sleep(2000)

2.getId()方法

    作用是取得线程的唯一标识。

3.停止线程

    a.使用Thread.stop()方法,但是这个方法是废弃的。

    b.使用Thread.interrupt()方法,但这个方法不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的终止。

    c.异常也可以停止线程。

    三种方法:

    1)、使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

    2)、使用stop方法强行终止线程,但是不推荐使用这个方法,因为stop和suspend及resume一样,都是作废过期的方法,使用它们可能产生不可预料的结果。

    3)、使用interrupt方法中断线程。

--------------------------------------------------------------------------------------------------

使用interrupt方法的效果并不像for+break语句那样,马上就停止,调用interrupt方法只是在当前线程打了一个停止的标记,并不是真的停止线程。代码:

Thread thread = new Thread();thread.start();thread.interrupt();//打下标记

判断线程是否是停止的状态

a.this.interruptted():测试当前线程是否已经中断,执行之后具有将状态标志清除为false的功能。

b.this.isInterrupted():测试线程是否已经中断,但不清除状态。

4.yield方法

    作用是放弃当前的cpu资源,将它让给其他的任务去占用CPU执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。

Thread.yield()

5.线程的优先级

    设置线程的优先级使用setPriority()方法。线程的优先级分为1~10个等级,如果小于1或大于10,则JDK抛出异常throw new IllegalArgumentException().

Thread.currentThread().setPriority(6);

注意:线程的优先级具有随机性。

6.守护线程

典型的守护线程就是垃圾回收线程,当进程中没有守护线程了,则垃圾回收线程也就没有存在的必要了,自动销毁。守护即线程的作用是为其他线程的运行提供便利服务,最典型的应用就是GC。

Thread thread = new Thread();thread.setDaemon(true);//设置为守护线程

二、对象及变量的并发访问(synchronized

1.synchronized同步方法

a.方法加synchronized

private int num;	synchronized public void add(String name){		try{			if(name.equals("b")){				 num =100;				System.out.println(num);			}else{				num = 200;				System.out.println(num);			}		}catch(InterruptedException e){			e.printStackTrace();		}	}

关键字synchronized取得的所都是对象锁,而不是一段代码或方法(函数)当作锁,所以那个线程先执行带synchronized关键字的方法,那个线程就持有该方法所属对象的锁Lock,其他线程只能呈等待状态,但是前提是多个线程访问的是同一个对象。

注意:set和get方法,如果set方法上有synchronized修饰,则get方法上也应该加上,不然就出现数据不一致。

2.synchronized锁重入

    概念是:自己可以再次获取自己的内部锁。比如有1条线程获得了某个对象的锁,此时这个对象锁还没有释放,当其次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。

synchronized public void service1(){		System.out.println("service1");		service2();	}	synchronized public void service2(){		System.out.println("service2");	}

同时,当存在父子类继承关系时。子类完全可以通过“可重入锁”调用父类的同步方法,但是锁不具有继承性。假如线程出现了异常,锁自动释放。

3.synchronized同步代码块

public void service3(){		synchronized (this) {			System.out.println("service3");		}	}

    在使用同步synchronized(this)代码块时需要注意的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞,这说明synchronized使用的是“对象监视器”。同时代码块也是锁定当前对象的。

      多个线程调用同一个对象中的不同名称的synchronized同步方法或synchronized(this)同步代码块时,调用的效果就是按顺序执行,也就是同步的,阻塞的。

(1)、synchronized同步方法

a.对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态。

b.同一时间只有一个线程可以执行synchronized同步方法中的代码。

同理synchronized代码块也是一样的。

(2)、同步代码块

支持任意对象作为对象监视器来实现同步的功能。这个“任意对象”大多数是实例变量及方法的参数,使用格式为synchronized(非this对象)。

a.在多线程持有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非对象x)同步代码块中的代码

b.当持有“对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象X)同步代码中的代码

//同步代码对象	class Service{	   private String anyString = new String();//如果这个放进setString方法里面就会出现数据的不一致。	   public void setString(String userName){		   synchronized (anyString) {			System.out.println("业务处理");			try {				Thread.sleep(20000);			} catch (InterruptedException e) {				// TODO Auto-generated catch block				e.printStackTrace();			}		}	   }	}	class ThreadA extends Thread{		private Service service ;				public ThreadA(Service service) {			super();			this.service = service;		}		public void run(){			System.out.println("业务处理A");			service.setString("a");		}	}		class ThreadB extends Thread{		private Service service ;				public ThreadB(Service service) {			super();			this.service = service;		}		public void run(){			System.out.println("业务处理B");			service.setString("B");		}	}		public static void main(String[] args) {		Service service = new Service();		ThreadA threadA = new ThreadA(service);		threadA.setName("a");		threadA.start();		ThreadB threadB = new ThreadB(service);		threadB.setName("B");		threadB.start();	}

总结:synchronized(非this对象X)格式的写法是将X对象本身作为对象监视器,以下是结论。

a.当多个线程同时执行synchronized(X){}同步代码块时呈同步效果。

b.当其他线程执行X对象中synchronized同步时呈同步效果。

c.当其他线程执行X对象方法时里面的synchronized(this)代码块时也呈现同步效果。

静态同步synchronized和同步synchronized 的区别:synchronized关键字加到static静态方法上是给Class类上锁,而synchronized关键字加到非static静态方法上是给对象加锁。

注意:

将synchronized(String) 同步块与String联合使用时,需要注意的是因为JVM会为String进行缓存,所以会出现线程不能执行的问题。所以同步synchronized代码块时都不使用String作为锁对象,而改用其他,比如new Object()实例一个Object对象,它不会放入缓存

总结:

关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法或一个代码块。它包含两个特征:互斥性和可见性。同步synchronized不仅可以解决一个线程看到对象处于不一致的状态,还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护之前的修改成果。

4.死锁

4.1 同步synchronized方法无线等待与解决

死锁举例代码一:

synchronized public void service1(){		System.out.println("service1");				boolean isCount = true;		while(isCount){					}		System.out.println("service1 end");			}

死锁举例代码二:

public class test2 implements Runnable{	public Object lock1 = new Object();		public Object lock2 = new Object();	public String uname;		public void setUname(String uname) {		this.uname = uname;	}	@Override	public void run() {		if(uname.equals("a")){			synchronized (lock1) {								try {					System.out.println(uname);					Thread.sleep(20000);				} catch (InterruptedException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}								synchronized (lock2) {					System.out.println("lock2");				}							}		}						if(uname.equals("b")){			synchronized (lock2) {								try {					System.out.println(uname);					Thread.sleep(20000);				} catch (InterruptedException e) {					// TODO Auto-generated catch block					e.printStackTrace();				}								synchronized (lock1) {					System.out.println("lock1");				}							}		}					}}

总结:1.while循环加上推荐语句,容易出现。

           2.双方互相有对方的锁的情况。

三、对象及变量的并发访问(volatile

1.volatile相关的概念和作用

关键字volatile的主要作用是使变量在多个线程间可见。

解决上面死锁的问题。

class test extends Thread{    volatile private boolean isRuning = true;		public boolean isRuning(){		return isRuning;	}	   public void run(){        while(isRuning == true){    }   }}

关键字volatile的作用是强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值。

使用volatile关键字增加了实例变量在多个线程之间的可见性。但volatile关键字最致命的缺点是不支持原子性。

注意:改变实例变量中的数据,比如i++,并不是原子操作,也就不是线程安全的,详细步骤分解为:

a.从内存中取出i的值;b.计算i的值;c.将i的值写到内存中。步骤b容易出息问题。

总结:对于volatile修饰的变量,JVM虚拟机只是保证从内存加载到线程工作内存的值是最新的,也就是说,volatile关键字解决的是变量读时的可见性问题,但无法保证原子性,对于多个线程访问同一个实例变量还是需要同步加锁。

对于上面的i++,JDK 提供了AtomicInteger原子类进行实现。相关的API在util包下面。

2.volatile和synchronized的对比

a.关键字volatile是线程同步的轻量级实现,所以volatile性能比synchronized要好,并且volatile只能修饰变量,而synchronized可以修饰方法,以及代码块。

b.多线程访问volatile不会发生阻塞,而synchronized会出现阻塞。

c.volatile能保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步。

 

转载于:https://my.oschina.net/u/2380961/blog/1594040

你可能感兴趣的文章
linux中搭建ftp以root用户连接时500 OOPS: cannot change directory:/root报错
查看>>
Docker 快速学习
查看>>
http相关面试题汇总
查看>>
如何处理服务器SSL收到了一个弱临时Diffie-Hellman 密钥?
查看>>
ajax注册页面异步验证
查看>>
linux(VMware里)不重启添加新硬盘
查看>>
java之System类
查看>>
nginx 常用命令整理
查看>>
在路由器配置telnet
查看>>
tcp三次握手
查看>>
Tomcat基本结构和配置文件结构
查看>>
在腾讯云上把Laravel整合万向优图图片管理能力,打造高效图片处理服务
查看>>
爱创课堂每日一题八十一天-行内元素和块级元素的具体区别是什么?行内元素的pad...
查看>>
数据库(二)
查看>>
php抽奖概率算法
查看>>
20190202 week2
查看>>
漫谈Web缓存架构
查看>>
管理系统中的mysql基本应用
查看>>
高级持续威胁(APT)终结者-Log 360
查看>>
【初学小白】课程作业 11台路由器配置静态路由实现全网互通
查看>>