synchronized和lock的区别和使用
线程与进程
一个程序至少需要一个线程,一个进程至少需要一个线程 线程->进程->程序
线程是程序执行流的最小单位,进程是系统进行资源分配和调度的一个独立单位。
Thread的方法
①start()方法:开始执行该线程
②stop()方法:强制结束该线程
③join()方法 :等待该线程结束
④sleep()方法:该线程进入等待
⑤run()方法 :直接执行该线程的run方法(线程调用start()也会执行run方法,区别是一个是由线程调度运行run 方法,一个是直接调用线程中的run方法)
注意:wait()和notify()是object中的方法,分别表示线程挂起和线程恢复
wait()与sleep()的区别:wait()会释放对象锁,sleep()不会释放对象锁
线程的5大状态与转换
①新建状态:新建线程对象,并没有调用start之前;
②就绪状态:调用start方法之后就进入就绪状态,另外线程在睡眠和挂起中恢复的时候也会进入就绪状态;
③运行状态:线程被设置为当前线程开始执行run方法;
④阻塞状态:线程被暂停,比如调用sleep方法后;
⑤死亡状态:线程执行结束。
锁的类型
①可重入锁:在执行对象中所有同步方法不用再次获得锁
②可中断锁:在等待获取锁过程中可中断
③公平锁:按等待获取锁的线程的等待时间进行获取,等待时间长的具有优先获取锁的权力
④读写锁:对资源读取和写入的时候拆分为2部分处理,读的时候可以多线程一起读,写的时候必须同步的写
java中每个对象都可作为锁,锁有四种级别,按照量级从轻到重分为:无锁,偏向锁,轻量级锁,重量级锁。每个对象一开始都是无锁的,随着线程间争夺锁,越激烈,锁的级别越高,并且锁只能升级不能降级。
synchronized和lock的区别
synchronized
存在层次:Java的关键字,在jvm层面上;
锁的释放:1.以获取锁的线程执行同步代码,释放锁;
2.线程执行发生异常,jvm会让线程释放锁;
锁的获取:假设A线程获得锁,B线程等待,如果A线程阻塞,B线程会一直等待;
锁状态:无法判断;
锁类型:可重入,不可中断,非公平;
性能:少量同步
示例:
package day24;
public class Test4 {
public static void main(String[] args) {
MyThread5 m = new MyThread5();
Thread m1 = new Thread(m);
Thread m2 = new Thread(m);
m1.start();
m2.start();
}
}
class MyThread5 implements Runnable{
private int id;
private Object key = new Object();
@Override
public synchronized void run(){
for (int i = 0; i < 1000000; i++) {
id ++;
}
System.out.println(Thread.currentThread().getName()+"结果是:"+ id);
}
}
运行结果:
lock
存在层次:是一个类
锁的释放:在finally中必须释放锁,不然容易造成线程死锁
锁的获取:分情况而定,Lock有多个锁获取的方式,可以尝试获得锁
锁状态:可以判断
锁类型:可重入,可判断,可公平(两者皆可)
性能:大量同步
示例:
package day24;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
MyThread6 m = new MyThread6();
Thread m1 = new Thread(m);
Thread m2 = new Thread(m);
m1.start();
m2.start();
}
}
class MyThread6 implements Runnable{
private int id;
private Object key= new Object();
Lock lock = new ReentrantLock();
@Override
public void run(){
for (int i = 0; i < 1000000; i++) {
try{
//加锁
lock.lock();
id++;
}finally {
//释放锁
lock.unlock();
}
}
System.out.println(Thread.currentThread().getName()+"结果是:"+ id);
}
}
运行结果:
synchronized作用
synchronized可以保证方法或者代码块在运行时同一时刻只有一个线程能进入临界区,同时保证共享变量对其他线程的可见性
JDK1.6之前Synchronized是一个重量级锁,是通过对象内部的一个叫做监视器锁(monitor)来实现的,但是监视器本质又是依赖于底层的操作系统的Mutex Lock来实现的,而操作系统实现线程之间的切换这就需要从用户态转换到核心态
优化:
①线程自旋和适应性自旋
java线程其实是映射在内核之上的,线程的挂起和恢复会极大的影响开销,并且jdk官方人员发现,很多线程在等待锁的时候,在很短的一段时间就获得了锁,所以他们在线程等待的时候,并不需要把线程挂起,而是让他无目的循环,一般设置10次,这样就避免了线程切换的开销,极大的提升了性能。而自适应自旋,是赋予自旋一种学习能力,它并不固定自旋10次一下。他可以根据它前面线程的自旋情况,从而调整它的自旋。甚至是不经过自旋而直接挂起
②锁消除
锁消除就是把不必要的同步在编译阶段进行移除,这里的锁消除并不一定指你写的代码的锁消除。
比如stringBuffer是一个安全的类,它在执行一个简单的append方法拼接字符串,append方法都会同步,但分析可以知道这并不存在线程安全问题,这时就会把这个同步锁消除
③锁粗化
在用synchronized时,都讲究避免大开销,尽管同步代码块要小,为什么还要加粗呢。哪stringBuffer拼接字符串来说,每一个append都需要同步一次,那就可以把锁粗化到第一个和最后一个append
④轻量级锁
⑤偏向锁