在多线程编程过程中,那一定避免不了线程安全的问题。由于每个线程执行过程是不可控的,所以很可能导致最终的结果与实际上的愿望相违背或者直接导致程序出错。
那么怎么解决线程安全的问题呢?
基本上所有的并发模式在解决线程安全的问题时,都是采用互斥访问的方式来解决,就是在同一时刻,只能由一个线程访问共享的资源。也就是在访问共享资源的代码之前加一个锁,当线程访问该资源时持有这个锁,等访问结束释放这把锁,这样别的线程拿到这把锁继续访问。
在java中提供了两种方式来实现同步互斥访问,分别是synchronized和Lock,Lock放在下一篇来讲述。
synchronized同步方法和代码块
在java中,可以用synchronized关键字来标记一个方法或者代码块。当一个线程调用一个对象的synchronized方法或者代码块时,这个线程就拿到这个对象的锁,其他线程暂时无法访问这个方法或者代码块,只有等待这个方法或者代码块执行完成之后,这个线程才会释放这个对象的锁,这样别的线程才能拿到该对象锁来访问其方法或代码块。
下面通过几个例子来讲解synchronized的使用方式:
- 同步方法
- 同步非静态方法
private synchronized void test(String thread) {
for (int i = 0; i < 10; i++)
try {
Thread.sleep(100);
System.out.println(thread + "正在访问");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
注意:这里的sleep了100ms是为了测试,如果不暂停一下,两个线程同时执行,看不到交叉访问的情况,因为CPU还没来得及调度。
2.同步静态方法
private synchronized static void test(String thread) {
for (int i = 0; i < 10; i++)
try {
Thread.sleep(100);
System.out.println(thread + "正在访问");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized非静态方法时持有的是调用这个方法的对象锁,那么多个线程只有是同一个对象才能保证访问这个方法是互斥的。synchronized静态方法时持有的是这个方法所在的类,也就是说多个线程多个对象访问这个方法都是互斥的。
- 同步代码块
同步方法不够灵活,因为方法中可能有部分代码不需要同步,这个时候可以用synchronized来同步只需要同步的代码块。
private void test(String thread) {
synchronized (this) {
for (int i = 0; i < 10; i++)
try {
Thread.sleep(100);
System.out.println(thread + "正在访问");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
其中,synchronized(obj),这里的obj可以是this关键字、可以是类中的一个属性,也可以是类名.class。
那么这三种到底有什么区别呢?
- this关键字和类的属性,和用synchronized修饰普通方法同步效果类似。
- 类名.class,和用synchronized修饰静态方法同步效果类似。
网友评论