在现代计算机中每每存在多个CPU
核心,而1
个CPU
能同时运行一个线程,为了充分利用CPU
多核心,提升CPU
的效率,多线程就应时而生了。java
那么多线程就必定比单线程快吗?答案是不必定,由于多线程存在单线程没有的问题编程
CPU
给每一个线程分配的时间片很短,一般是几十毫秒(ms),那么线程的切换就会很频繁。A
和线程B
都在互相等待对方释放锁,死锁会形成系统不可用。1Mb/s
,资源的服务器带宽只有2Mb/s
,那么开10
个线程下载资源并不会将下载速度提高到10Mb/s
。既然多线程存在这些问题,那么咱们在开发的过程当中有必要使用多线程吗?咱们知道任何技术都有它存在的理由,总而言之就是多线程利大于弊,只要咱们合理使用多线程就能达到事半功倍的效果。bash
多线程的意思就是多个线程同时工做,那么多线程之间如何协同合做,这也就是咱们须要解决的线程通讯、线程同步问题服务器
synchronized
是Java的关键字,可用于同步实例方法、类方法(静态方法)、代码块多线程
synchronized
修饰实例方法的时候,同步的范围是当前实例的实例方法。synchronized
修饰类方法的时候,同步的范围是当前类的方法。synchronized
修饰代码块的时候,同步的范围是()
中的对象。"talk is cheap show me the code"
让咱们分别运行个例子来看看。并发
synchronized public void synSay() {
System.out.println("synSay----" + Thread.currentThread().getName());
while (true) { //保证进入该方法的线程 一直占用着该同步方法
}
}
public void say() {
System.out.println("say----" + Thread.currentThread().getName());
}
public static void main(String[] args){
Test test1 = new Test();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
test1.synSay();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000); //休眠3秒钟 保证线程t1先执行
} catch (InterruptedException e) {
e.printStackTrace();
}
test1.say();
test1.synSay();
}
});
t1.start();
t2.start();
}
复制代码
运行输出ide
synSay----Thread-0 //线程t1
say----Thread-1 //线程t2
复制代码
建立t1
,t2
两个线程,分别执行同一个实例test1
的方法,线程t1
先执行加了同步关键字的synSay
方法,注意方法里面须要加上个while
死循环,目的是让线程一直在同步方法里面,而后然线程t1执行以后再让线程t2去执行,此时线程t2并不能成功进入到synSay
方法里面,由于此时线程t1正在方法里面,线程2只能在synSay
方法外面阻塞,可是线程t2能够进入到没有加同步关键字的say
方法。
也就是说关键字synchronized
修饰实例方法的时候,锁住的是该实例的加了同步关键字的方法,而没有加同步关键字的方法,线程仍是能够正常访问的。可是不一样实例之间同步是不会影响的,由于每一个实例都有本身的一个锁,不一样实例之间的锁是不同的。学习
synchronized static public void synSay() {
System.out.println("static synSay----" + Thread.currentThread().getName());
while (true) { //保证进入该方法的线程 一直占用着该同步方法
}
}
synchronized public void synSay1() {
System.out.println("synSay1----" + Thread.currentThread().getName());
}
public void say() {
System.out.println("say----" + Thread.currentThread().getName());
}
public static void main(String[] args){
Test test1 = new Test();
Test test2 = new Test();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
test1.synSay();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000); //休眠3秒钟 保证线程t1先执行
} catch (InterruptedException e) {
e.printStackTrace();
}
test1.say();
test2.say();
test1.synSay();
}
});
t1.start();
t2.start();
}
复制代码
运行输出ui
static synSay----Thread-0 //线程t1 实例test1
say----Thread-1 //线程t2 实例test1
say----Thread-1 //线程t2 实例test2
static synSay----Thread-0 //线程t1 实例test1
say----Thread-1 //线程t2 实例test1
synSay1----Thread-1 //线程t2 实例test1
say----Thread-1 //线程t2 实例test2
复制代码
这里和上面的同步实例方法的代码差很少,就是将synSay
方法加上了static
修饰符,即把方法从实例方法变成类方法了,而后咱们再新建个实例test2
,先让线程t1调用实例test1的synSay类方法,在让线程t2去调用实例test1的say实例方法、synSay类方法和让线程t2去调用实例test2的say实例方法,发现在线程t1占用加了同步关键字的synSay
类方法的时候,别的线程是不能调用加了锁的类方法的,可是能够调用没有加同步关键字的方法或者加了同步关键字的实例方法,也就是说每一个类有且仅有11
个锁,每一个实例有且仅有1
个锁,可是每一个类能够有一个或者多个实例,类的锁和实例的锁不会相互影响,实例之间的锁也不会相互影响。须要注意的是,一个类和一个实例有且仅有一个锁,当这个锁被其余线程占用了,那么别的线程就没法得到锁,只有阻塞等待。this
public void synSay() {
String x = "";
System.out.println("come in synSay----" + Thread.currentThread().getName());
synchronized (x) {
System.out.println("come in synchronized----" + Thread.currentThread().getName());
while (true) { //保证进入该方法的线程 一直占用着该同步方法
}
}
}
public static void main(String[] args){
Test test1 = new Test();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
test1.synSay();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000); //休眠3秒钟 保证线程t1先执行
} catch (InterruptedException e) {
e.printStackTrace();
}
test1.synSay();
}
});
t1.start();
t2.start();
}
复制代码
运行输出
come in synSay----Thread-0
come in synchronized----Thread-0
come in synSay----Thread-1
复制代码
能够发现同步代码块和同步实例方法、同步类方法其实差很少,可是同步代码块将同步的范围缩小了,能够同步到指定的对象上,而不像同步实例方法、同步类方法那样同步的是整个方法,因此同步代码块在效率上比其余二者都有较大的提高。
须要注意的是,当同步代码块的时候,在类方法中加入同步代码块且同步的对象是xx.class
等类的引用的时候,同步的是该类,若是在****实例方法中加入同步代码块且同步的对象是this
,那么同步的是该实例,能够当作前者使用的是类的锁**,后者使用的是实例的锁。
建议把volatile
的特性和synchronized
的特性进行对比学习,加深理解。《Java volatile关键字解析》
JMM
关于synchronized
的两条语义规定了:
大概流程:清空线程的工做内存->在主存中拷贝变量副本到工做内存->执行完毕->将变量副本写回到主存中->释放锁。
因此synchronized
能保证共享变量的可见性,而实现这个流程的原理也是经过插入内存屏障,和关键字volatile
类似。
由于synchronized
是给共享变量加锁,即便用阻塞的同步机制,共享变量只能同时被一个线程操做,因此JMM
不用像volatile
那样考虑加内存屏障去保证synchronized
多线程状况下的有序性,由于CPU
在单线程状况下是保证了有序性的。
因此synchronized
修饰的代码,是保证了有序性的。
一样由于synchronized
是给共享变量加锁了,以阻塞的机制去同步,在对共享变量进行读/写操做的时候是原子性的。
因此synchronized
修饰的代码,是能保证原子性的。
Java并发编程的艺术
内存可见性和原子性:Synchronized和Volatile的比较
java synchronized类锁,对象锁详解(转载)
原文地址:ddnd.cn/2019/03/21/…