小知识,大挑战!本文正在参与“ 程序员必备小知识 ”创作活动
作者的其他平台:
| CSDN:blog.csdn.net/qq_4115394…
| 知乎:www.zhihu.com/people/1024…
| GitHub:github.com/JiangXia-10…
| 公众号:1024笔记
本文大概15558字,读完共需35分钟
1 前言
上一篇文章讲了多线程编程中Synchronized同步方法的相关内容,Synchronized除了同步方法之外还可以同步语句块,这篇文章就介绍Synchronized如何同步语句块。
2 正文
1、Synchronized同步方法的缺点
在介绍Synchronized同步语句块之前,先来说说Synchronized同步方法的缺点。先看代码:
public class Demo9 {
public static void main(String[] args) throws InterruptedException {
Demo9Service service = new Demo9Service();
Thread t1 = new Demo9ThreadA(service);
t1.setName("A");
Thread t2 = new Demo9ThreadA(service);
t2.setName("B");
t1.start();
t2.start();
Thread.sleep(20000);
long start = Demo9Untils.start1 > Demo9Untils.start2 ? Demo9Untils.start2 : Demo9Untils.start1;
long end = Demo9Untils.end1 > Demo9Untils.end2 ? Demo9Untils.end1 : Demo9Untils.end2;
System.out.println("总耗时:" + (end - start) / 1000 + "秒");
}
}
class Demo9Untils{
static long start1;
static long start2;
static long end1;
static long end2;
}
class Demo9Service{
synchronized public void foo(){
try{
System.out.println("开始任务");
Thread.sleep(5000);
System.out.println("长时任务处理完成,线程" + Thread.currentThread().getName());
System.out.println("结束任务");
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
class Demo9ThreadA extends Thread{
public Demo9Service service;
public Demo9ThreadA(Demo9Service service){
this.service = service;
}
@Override
public void run() {
Demo9Untils.start1 = System.currentTimeMillis();
service.foo();
Demo9Untils.end1 = System.currentTimeMillis();
}
}
class Demo9ThreadB extends Thread{
public Demo9Service service;
public Demo9ThreadB(Demo9Service service){
this.service = service;
}
@Override
public void run() {
Demo9Untils.start2 = System.currentTimeMillis();
service.foo();
Demo9Untils.end2 = System.currentTimeMillis();
}
}
结果:
2、Synchronized同步代码块
再来看看synchronized同步代码块的代码:
public class Demo10 {
public static void main(String[] args) {
Demo10Service service = new Demo10Service();
Thread t1 = new Demo10Thread(service);
t1.setName("A");
t1.start();
Thread t2 = new Demo10Thread(service);
t2.setName("B");
t2.start();
}
}
class Demo10Service{
public void foo(){
try {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + "开始于" + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "结束于" + System.currentTimeMillis());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class Demo10Thread extends Thread{
private Demo10Service service;
public Demo10Thread(Demo10Service service){
this.service = service;
}
@Override
public void run() {
service.foo();
}
}
结果:
通过上面的示例可以发现Synchronized同步方法时间耗费过长。
3、使用同步代码块解决同块方法的问题
那么如何使用同步代码块解决同步方法的问题,代码如下:
public class Demo11 {
public static void main(String[] args) throws InterruptedException {
Demo11Service service = new Demo11Service();
Thread t1 = new Demo11ThreadA(service);
t1.setName("A");
t1.start();
Thread t2 = new Demo11ThreadB(service);
t2.setName("B");
t2.start();
Thread.sleep(10000);
long start = Demo11Utils.start1 > Demo11Utils.start2 ? Demo11Utils.start2 : Demo11Utils.start1;
long end = Demo11Utils.end1 > Demo11Utils.end2 ? Demo11Utils.end1 : Demo11Utils.end2;
System.out.println("耗时:" + (end - start) /1000 + "秒");
}
}
class Demo11Utils{
static long start1;
static long start2;
static long end1;
static long end2;
}
class Demo11Service{
/*synchronized*/ public void foo(){
try {
System.out.println(Thread.currentThread().getName() + "开始任务");
Thread.sleep(3000);
synchronized (this) {
System.out.println(Thread.currentThread().getName() + "处理计算结果");
}
System.out.println(Thread.currentThread().getName() + "结束任务");
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
class Demo11ThreadA extends Thread{
private Demo11Service service;
public Demo11ThreadA(Demo11Service service){
this.service = service;
}
@Override
public void run() {
Demo11Utils.start1 = System.currentTimeMillis();
service.foo();
Demo11Utils.end1 = System.currentTimeMillis();
}
}
class Demo11ThreadB extends Thread{
private Demo11Service service;
public Demo11ThreadB(Demo11Service service){
this.service = service;
}
@Override
public void run() {
Demo11Utils.start2 = System.currentTimeMillis();
service.foo();
Demo11Utils.end2 = System.currentTimeMillis();
}
}
结果:
可见耗时明显缩短!
4、synchronized代码块间的同步
/**
* synchronized代码块的同步
*/
public class Demo13 {
public static void main(String[] args) throws InterruptedException {
DemoService13 service13 = new DemoService13();
Thread t1 = new Demo13Thread1(service13);
t1.start();
Thread.sleep(100);
Thread t2 = new Demo13Thread2(service13);
t2.start();
}
}
//声明一个服务
class DemoService13{
public void foo1(){
//同步代码块
synchronized (this){
//睡眠2s
try {
System.out.println("foo1方法开始时间:"+System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("foo1方法结束时间:"+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void foo2(){
synchronized (this){
System.out.println("foo2方法开始时间: "+System.currentTimeMillis());
System.out.println("foo2方法结束时间: "+System.currentTimeMillis());
}
}
}
//声明一个子线程
class Demo13Thread1 extends Thread{
private DemoService13 service13;
//构造方法
public Demo13Thread1(DemoService13 service){
this.service13 = service;
}
public void run(){
service13.foo1();
}
}
//再声明一个子线程
class Demo13Thread2 extends Thread{
private DemoService13 service13;
//构造方法
public Demo13Thread2(DemoService13 service){
this.service13 = service;
}
public void run(){
service13.foo2();
}
}
结果:
使用同步synchronize(this)代码时需要注意,当一个线程访问object的一个synchronized(this)同步代码块时,其它线程对这个object的其它syncrhonized(this)同步的访问会被阻塞,说明synchronized使用的对象锁是同一个。
synchroinze(this)代码块锁定的是当前对象。比如:
/**
*synchroinze(this)代码块是锁定当前对象
*/
public class Demo14 {
public static void main(String[] args) throws InterruptedException {
Demo14Service service = new Demo14Service();
Thread t1 = new Demo14ThreadA(service);
Thread t2 = new Demo14ThreadB(service);
t2.start();
Thread.sleep(10);
t1.start();
}
}
class Demo14Service{
synchronized public void foo1(){
System.out.println("foo1方法正在运行");
}
public void foo2(){
try {
synchronized (this) {
System.out.println("foo2方法开始");
Thread.sleep(2000);
System.out.println("foo2方法结束");
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class Demo14ThreadA extends Thread{
private Demo14Service service;
public Demo14ThreadA(Demo14Service service){
this.service = service;
}
@Override
public void run() {
service.foo1();
}
}
class Demo14ThreadB extends Thread{
private Demo14Service service;
public Demo14ThreadB(Demo14Service service){
this.service = service;
}
@Override
public void run() {
service.foo2();
}
}
结果:
synchronized(this)代码块与synchronized方法一样,synchronized(this)代码块也是锁定当前对象的。
5、使用任意对象作为对象锁
除了可以使用syncrhonized(this)来同步代码块,java还支持任意对象作为对象锁来实现同步的功能。这个任意对象就是成员变量或者方法中的参数,语法格式为:
synchronized(lockobj)
在多个线程持有的对象锁为同一个对象的情况下,同一时间只有一个线程可以执行synchoronized(lockobj)同步代码块中的代码。如果使用的不是同一个对象锁,运行的结果就是异步调用,交叉输出结果。
比如:
/**
* 使用任意对象作为对象锁
*/
public class Demo15 {
public static void main(String[] args) {
Demo15Service service = new Demo15Service();
Thread t1 = new Demo15Thread(service);
t1.setName("线程1");
t1.start();
Thread t2 = new Demo15Thread(service);
t2.setName("线程2");
t2.start();
}
}
class Demo15Service{
//锁对象
private Object lockobject = new Object();
public void foo(){
//synchronized锁定对象
synchronized (lockobject){
try {
System.out.println(Thread.currentThread().getName() + "开始于" + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "结束于" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Demo15Thread extends Thread{
private Demo15Service service;
public Demo15Thread(Demo15Service service){
this.service = service;
}
@Override
public void run() {
service.foo();
}
}
结果:
与synchronized (this)相比非tihs对象具有一定的优点。
一个类有很多个synchrnoized方法,虽然可以实现同步,但是会受到阻塞,影响运行效率。如果使用同步代码块非this对象,则synchronized非this对象的代码块中的程序与同步方法是不与其它的锁(this)争抢this锁,大大提高运行的效率。
/**
* 非this对象具有的优点
*/
public class Demo16 {
public static void main(String[] args) throws InterruptedException {
Demo16Service service = new Demo16Service();
Thread t1 = new Demo16ThreadA(service);
t1.start();
Thread.sleep(10);
Thread t2 = new Demo16ThreadB(service);
t2.start();
}
}
class Demo16Service{
private Object lockObject = new Object();
public void foo(){
try {
synchronized (lockObject) {
System.out.println("foo方法开始时间" + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("foot方法结束时间" + System.currentTimeMillis());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
synchronized public void foo2(){
System.out.println("foo2方法开始时间" + System.currentTimeMillis());
System.out.println("foo2方法结束时间" + System.currentTimeMillis());
}
}
class Demo16ThreadA extends Thread{
private Demo16Service service;
public Demo16ThreadA(Demo16Service service){
this.service =service;
}
@Override
public void run() {
service.foo();
}
}
class Demo16ThreadB extends Thread{
private Demo16Service service;
public Demo16ThreadB(Demo16Service service){
this.service = service;
}
@Override
public void run() {
service.foo2();
}
}
结果:
5、synchronized(非this对象)解决脏读
import java.util.ArrayList;
import java.util.List;
public class Demo17 {
public static void main(String[] args) throws InterruptedException {
Demo17List list = new Demo17List();
Thread t1 = new Demo17ThreadA(list);
t1.start();
Thread t2 = new Demo17ThreadB(list);
t2.start();
Thread.sleep(5000);
System.out.println("list size is " + list.size());
}
}
class Demo17List{
private List list = new ArrayList();
synchronized public void add(Object obj){
list.add(obj);
}
synchronized public int size(){
return list.size();
}
}
class Demo17Service{
private Object lockObject = new Object();
public void add(Demo17List list, Object obj){
try {
synchronized (list) {
if (list.size() < 1) {
Thread.sleep(2000);
list.add(obj);
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class Demo17ThreadA extends Thread{
private Demo17List list;
public Demo17ThreadA(Demo17List list){
this.list = list;
}
@Override
public void run() {
Demo17Service service = new Demo17Service();
service.add(list, "a");
}
}
class Demo17ThreadB extends Thread{
private Demo17List list;
public Demo17ThreadB(Demo17List list){
this.list = list;
}
@Override
public void run() {
Demo17Service service = new Demo17Service();
service.add(list, "b");
}
}
结果:
对于synchronized(lockobj)格式的写法是将lockobj本身作为对象锁,总结上面的结果这样得出以下的结论:
a. 当多个线程同时执行syncrhonized(lockobj)同步代码块时结果是同步效果;
b. 当其它线程执行lockobj对象中的synchronized同步方法时也是同步效果;
c. 当其它线程执行lockobj对象方法里的synchronize(this)代码块时也是同步效果
如果其它线程调用不加synchronized关键字的方法时,还是异步调用。
6、半异步半同步
在并发模式中的同步和异步与IO模型中的同步和异步是完全不同的概念。
在IO模型中,同步和异步区分的是内核向应用程序通知的是何种IO事件(是就绪事件还是完成事件),以及该由谁来完成IO读写(是应用程序还是内核)。
而在并发模式中的同步指的是程序完全按照代码序列的顺序执行,异步指的是程序的执行需要由系统事件来驱动。常见的系统事件包括中断信号等。
按照同步方式运行的线程称为同步线程,按照异步方式运行的线程成为异步线程。显然异步线程的执行效率高,实时性强,这是很多嵌入式程序采用的模型。但编写异步方式执行的程序相对复杂,难于调试和扩展,且不适合大量的并发。而同步线程则相反,它虽然效率比较低,实时性较差,但逻辑简单。因此,对于某些既要求较好的实时性,又要求同时处理多个客户请求的应用程序,就可以同时使用同步线程和异步线程来实现。即使用半同步/半异步模式来实现!
如果使用synchronized关键字来实现半同步/半异步代码如下:
/**
* 半同步和半异步
*/
public class Demo12 {
public static void main(String[] args) {
Demo12Service service = new Demo12Service();
Thread t1 = new Demo12Thread(service);
t1.setName("A");
t1.start();
Thread t2 = new Demo12Thread(service);
t2.setName("B");
t2.start();
}
}
class Demo12Service {
public void foo(){
try{
for (int i = 0; i < 100; i++) {
System.out.println("非同步线程" + Thread.currentThread().getName() + ", i=" + i);
Thread.sleep(10);
}
System.out.println();
synchronized (this){
for (int i = 0; i < 100; i++) {
System.out.println("同步线程" + Thread.currentThread().getName() + ", i=" + i);
Thread.sleep(10);
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class Demo12Thread extends Thread{
private Demo12Service service;
public Demo12Thread(Demo12Service service){
this.service = service;
}
@Override
public void run() {
service.foo();
}
}
结果:
不在synchronized块中的就是异步执行,在synchroinzed块中的代码就是同步执行。
非同步的输出是交叉的,而同步线程只有线程a执行完了才执行线程B。
7、synchronized同步静态方法与synchronized(class)代码块
前面讲了synchronized关键字修饰方法,但是并没有讲修饰静态方法,关键字synchronized还可以修饰静态方法,synchronized修饰静态方法就是对象当前的*.java文件对应的Class进行加锁。比如:
public class Demo18 {
public static void main(String[] args) {
Thread t1 = new Demo18ThreadA();
t1.setName("A");
t1.start();
Thread t2 = new Demo18ThreadB();
t2.setName("B");
t2.start();
}
}
class Demo18Service{
synchronized public static void foo1(){
System.out.println(Thread.currentThread().getName() + "进入方法foo1在" + System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束方法foo1在" + System.currentTimeMillis());
}
synchronized public static void foo2(){
System.out.println(Thread.currentThread().getName() + "进入方法foo2在" + System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束方法foo2在" + System.currentTimeMillis());
}
}
class Demo18ThreadA extends Thread{
@Override
public void run() {
Demo18Service.foo1();
}
}
class Demo18ThreadB extends Thread{
@Override
public void run() {
Demo18Service.foo2();
}
}
结果:
从上面的结果可以发现在静态方法上使用synchronized修饰与在非静态方法上使用的结果是一致的,都是同步运行。但是其实从本质来讲它们是有区别的,在静态方法上使用synchronized是给Class类上锁,而在非静态方法上使用syncrhonized是给对象上锁。
public class Demo19 {
public static void main(String[] args) {
Demo19Service service = new Demo19Service();
Thread t1 = new Demo19ThreadA(service);
t1.setName("A");
t1.start();
Thread t2 = new Demo19ThreadB(service);
t2.setName("B");
t2.start();
}
}
class Demo19Service{
synchronized public static void foo1(){
System.out.println(Thread.currentThread().getName() + "进入foo1方法在" + System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束foo1方法在" + System.currentTimeMillis());
}
synchronized public void foo2(){
System.out.println(Thread.currentThread().getName() + "进入foo2方法在" + System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束foo2方法在" + System.currentTimeMillis());
}
}
class Demo19ThreadA extends Thread{
private Demo19Service service;
public Demo19ThreadA(Demo19Service service){
this.service = service;
}
@Override
public void run() {
service.foo2();
}
}
class Demo19ThreadB extends Thread{
private Demo19Service service;
public Demo19ThreadB(Demo19Service service){
this.service = service;
}
@Override
public void run() {
service.foo1();
}
}
结果:
上面以异步方式运行的原因是因为持有锁是不一样的,非静态方法持有提对象锁,而静态方法持有的是Class锁,Class锁可以对类的所有对象实例起作用。
同步synchronized(class)代码块的作用其实和synchronized static方法的作用是一样的
public class Demo20 {
public static void main(String[] args) {
Demo20Service service = new Demo20Service();
Thread t1 = new Demo20ThreadA(service);
t1.setName("A");
t1.start();
Thread t2 = new Demo20ThreadA(service);
t2.setName("B");
t2.start();
}
}
class Demo20Service{
synchronized public static void foo1(){
System.out.println(Thread.currentThread().getName() + "进入foo1方法在" + System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束foo1方法在" + System.currentTimeMillis());
}
public static void foo2(){
synchronized (Demo20Service.class) {
System.out.println(Thread.currentThread().getName() + "进入foo1方法在" + System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束foo1方法在" + System.currentTimeMillis());
}
}
}
class Demo20ThreadA extends Thread{
private Demo20Service service;
public Demo20ThreadA(Demo20Service service){
this.service = service;
}
@Override
public void run() {
service.foo1();
}
}
class Demo20ThreadB extends Thread{
private Demo20Service service;
public Demo20ThreadB(Demo20Service service){
this.service = service;
}
@Override
public void run() {
service.foo2();
}
}
结果:
8、synchronized(String)*
当使用synchronized关键字将String作为锁对象时:
public class Demo21 {
public static void main(String[] args) {
Thread t1 = new Demo21ThreadA();
t1.setName("A");
t1.start();
Thread t2 = new Demo21ThreadB();
t2.setName("B");
t2.start();
}
}
class Demo21Service{
public static void foo1(String lockObject){
try {
synchronized (lockObject) {
while (true) {
System.out.println("线程" + Thread.currentThread().getName());
Thread.sleep(1000);
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
public static void foo2(Object lockObject){
try {
synchronized (lockObject) {
while (true) {
System.out.println("线程" + Thread.currentThread().getName());
Thread.sleep(1000);
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
class Demo21ThreadA extends Thread{
@Override
public void run() {
Demo21Service.foo1("AA");
}
}
class Demo21ThreadB extends Thread{
@Override
public void run() {
Demo21Service.foo2("AA");
}
}
结果:
通过上面结果可以发现只会打印线程B。只有线程B在运行的原因是,两个线程的对象锁都是使用AA,两个线程持有的相同的锁,所以造成线程B能运行。这就是String常量所带来的问题。所以不应该使用字符串类型作为对象锁,而应该使用其它类型,例如new Object(),对象就不会放入缓存 。
3 总结
多线程编程之线程的同步机制(上): Synchronized同步方法和本篇文章主要讲了在多线程编程中Synchronized关键字对于同步机制的作用。Synchronized关键字不仅仅能够修饰方法还能够修饰代码块。
如果觉得这篇文章不错对你有帮助,就点赞分享给更多的人吧!
相关推荐: