一、Thread类的方法使用
1.1 currentThread()方法
currentThread()方法可以返回代码被那个线程调用的信息。
1.2 isAlive()方法
判断当前线程是否处于活跃状态,活跃状态是指线程已经启动并且尚未终止。
1.3 sleep()方法
sleep()方法是让正在运行的线程在指定毫秒数内暂停。
1.4 getId()方法
获取线程的唯一标识。
1.5 停止线程
停止线程意味着在线程处理完任务之前停掉正在做的操作,也就是放弃当前操作。有以下三种方法终止正在运行中的线程:
- 使用退出标志,使线程正常退出,就是当run方法完成后终止线程;
- 使用stop方法钱箱终止线程,但是不推荐,因为stop和suspend及resume一样是过期作废方法;
- 使用interrupt中断线程。
1.6 interrupt方法
interrupt()方法的使用并不像for+break语句那样,马上就停止循环。调用interrupt()方法仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程。所以引出this.interrupted()和this.isInterrupted()方法。
- this.interrupted()是判断当前线程是否已经是中断状态,执行此方法后具有将线程的中断状态标志清除为false的功能;
- this.isInterrupted()是判断当前线程是否已经是中断状态,但是不清除状态标志。
所以使用interrupt()时需要判断线程是否有中断标志,在使用return或者抛异常的方式中断此线程。
1.7 yield()方法
yield()方法的作用的是放弃当前的CPU资源,将他让给其他的任务去占用CPU执行时间,但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。
1.8 线程的优先级
在操作系统中,线程可以划分优先级,优先级高的线程得到的CPU资源较多,也就是说CPU优先执行优先级高的线程。线程的优先级具有继承性,比如A线程启动B线程,则B线程的优先级与A线程的一样。
修改优先级的方法是setPriority()。
优先级高的线程总是大部分先执行,但不代表优先级高的线程全部先执行。线程优先级还具有“随机性”,也就是优先级高的线程不一定每次都先执行。
1.9 守护线程
在Java线程中有两种线程,一种是用户线程,另一种就是守护线程。守护线程具有陪伴的含义,当进程中不存在非守护线程了,则守护线程自动销毁。典型的守护线程就是垃圾回收线程。可以通过调用Thead类的setDaemon(true)方法设置当前的线程为守护线程。
注意事项:
- setDaemon(true)必须在start()方法前执行,否则会抛出IllegalThreadStateException异常;
- 在守护线程中产生的新线程也是守护线程
- 不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑。
二、synchronized关键字
2.1 变量的线程安全性
“非线程安全”问题存在于“实例变量中”,如果是方法内部私有的变量,则不存在“非线程安全”的问题。如果两个线程同时操作对象中的实例变量,会出现非线程安全的问题,解决方法是在方法上添加添加synchronized关键字控制同步。
2.2 多个对象多个锁
synchronized取得的锁都是对象锁,而不是把一段代码或方法当做锁。所以在上面的实例中,哪个线程先执行带synchronized关键字的方法,则哪个线程就持有该方法所属对象的锁Lock,那么其他线程只能呈等待状态,前提是多个线程访问的是同一个对象。但是如果多个线程访问多个对象,JVM会创建多个锁。
2.3 synchronized方法与所对象
通过上面我们知道synchronized取得的锁都是对象锁,而不是把一段代码或方法当做锁。如果多个线程访问的是同一个对象,哪个线程先执行带synchronized关键字的方法,则哪个线程就持有该方法,那么其他线程只能呈等待状态。如果多个线程访问的是多个对象则不一定,因为多个对象会产生多个锁。
如果多个线程访问的是同一个对象中的未被synchronized关键字修饰的方法,线程会异步调用未被修饰的方法。
2.4 脏读
在赋值的时候进行了同步,但在取值的时候可能会出现一些意想不到的意外,这种情况就是脏读。发生脏读的情况是在读取实例变量的时候,此值已经被其他线程修改。
解决方法是在方法上加上synchronized关键字。
当线程1调用一个对象的synchronized方法A时候,线程1就会获取该对象的锁,这是线程2是无法获取该对象的被synchronized修饰的任何方法,但是可以访问未被synchronized修饰的方法。
2.5 synchronized锁重入
在使用synchronized时,当一个线程得到一个对象的锁后,没有释放,再次请求该对象的锁是可以再次获取到的。一个对象中的synchronized修饰的方法可以访问该对象中其他被synchronized修饰的方法,如果不可重入锁会造成死锁。
2.6 出现异常,锁自动释放
当一个线程执行的代码出现异常时,其所有的锁会自动释放。
2.7 同步不具有继承性
如果父类有一个带synchronized关键字的方法,子类继承并重写了这个方法。 但是同步不能继承,所以还是需要在子类方法中添加synchronized关键字。
2.8 为什么要使用synchronized?
在并发编程中存在线程安全问题,主要原因有:
1.存在共享数据
2.多线程共同操作共享数据。关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见性)
2.9 synchronized锁什么,加锁的目的是什么?
synchronized锁的是对象,锁的的对象可能是this、临界资源对象、class类对象
加锁的目的是保证操作的原子性
三、synchronized同步语句块
3.1 synchronized方法的缺点
使用synchronized关键字声明方法有些时候是有很大的弊端的,比如我们有两个线程一个线程A调用同步方法后获得锁,那么另一个线程B就需要等待A执行完,但是如果说A执行的是一个很费时间的任务的话这样就会很耗时。
3.2 synchronized(this)同步代码块的使用
当一个线程访问一个对象的synchronized同步代码块时,另一个线程任然可以访问该对象非synchronized同步代码块。
时间虽然缩短了,但是大家考虑一下synchronized代码块真的是同步的吗?它真的持有当前调用对象的锁吗?
是的。不在synchronized代码块中就异步执行,在synchronized代码块中就是同步执行。
3.3 synchronized代码块间的同步性
当一个对象访问synchronized(this)代码块时,其他线程对同一个对象中所有其他synchronized(this)代码块代码块的访问将被阻塞,这说明synchronized(this)代码块使用的“对象监视器”是一个。
也就是说和synchronized方法一样,synchronized(this)代码块也是锁定当前对象的。
3.4 小结
- 其他线程执行对象中synchronized同步方法和synchronized(this)代码块时呈现同步效果;
- 如果两个线程使用了同一个“对象监视器”,运行结果同步,否则不同步.
四、静态同步synchronized方法与synchronized(class)代码块
synchronized关键字加到static静态方法和synchronized(class)代码块上都是是给Class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁。
静态同步synchronized方法与synchronized(class)代码块持有的锁一样,都是Class锁,Class锁对对象的所有实例起作用。synchronized关键字加到非static静态方法上持有的是对象锁。
五、数据类型String的常量池属性
在Jvm中具有String常量池缓存的功能
1 | String s1 = "a"; |
2 | String s2="a"; |
3 | System.out.println(s1==s2);//true |
字符串常量池中的字符串只存在一份! 即执行完第一行代码后,常量池中已存在 “a”,那么s2不会在常量池中申请新的空间,而是直接把已存在的字符串内存地址返回给s2。
因为数据类型String的常量池属性,所以synchronized(string)在使用时某些情况下会出现一些问题,所以尽量不要使用synchronized(string)而使用synchronized(object)。
参考: