引例:吃苹果比赛,3个人同时吃50个苹果,谁先拿到谁就吃,每个哦ing过都有编号。
问题:
多线程同时执行的时候可能出现不安全问题
当3个人同时拿到一个苹果,他们的编号就一样,当时主要看是谁先吃掉苹果
除非拿到苹果和吃掉苹果是连续同步执行,没有其他的线程干扰
方案一:
设置同步代码块(同步锁)。
synchronized(共享资源){
同步执行代码;
}
class Apple implements Runnable{
private int num=50;
public void run() {
for(int i=0;i<50;i++){
synchronized (this) {//!!!解决了不安全问题,这里的this表示Apple共同资源
//同步代码块
//——————————————————————————————————————————————
if(num>0){
try {
//导致一个资源信息被多个用户同时拿到
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"吃了编号为"+num+"的苹果");
num--;
//——————————————————————————————————————————————————
}
}
}
}
}
//Thread.currentThread().getName() 拿到当前线程的引用和名称
public class ImplementDemo {
public static void main(String[] args) {
Apple a=new Apple();
new Thread(a,"A").start();
new Thread(a,"B").start();
new Thread(a,"C").start();
}
}
方法二:
同步方法。
synchronized 方法名{
方法体(同步代码);
}
class Apple implements Runnable{
private int num=50;
public void run() {
for(int i=0;i<50;i++){
eat();
}
}
//——————————————————————————
synchronized private void eat() {
if (num > 0) {
try {
// 导致一个资源信息被多个用户同时拿到
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "吃了编号为" + num + "的苹果");
num--;
}
}
}
//Thread.currentThread().getName() 拿到当前线程的引用和名称
public class ImplementDemo {
public static void main(String[] args) {
Apple a=new Apple();
new Thread(a,"A").start();
new Thread(a,"B").start();
new Thread(a,"C").start();
}
}
————————————————————————————华丽的分割线——————————————————————————————
运用synchronized性能并不高,例如JAVA中的Stringbuilder和Stringbuffer的区别:Stringbuffer的append方法加了synchronized保证了安全性,但是性能却比Stringbuilder低,出现了两面性,所以尽量用Stringbuilder,遇到安全问题再做操作。
建议:尽量减小synchronized代码块的大小。
方法三:
锁机制。
public interface Lock
Lock
实现提供了比使用 synchronized
方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition
对象。
ReadWriteLock
的读取锁。
class Apple implements Runnable{
private int num=50;
//创建一个锁对象
private final Lock lock = new ReentrantLock();
public void run() {
for(int i=0;i<50;i++){
eat();
}
}
private void eat() {
//进入代码块之后立马加锁
lock.lock();//获取锁
if (num > 0) {
try {
System.out.println(Thread.currentThread().getName() + "吃了编号为" + num + "的苹果");
// 导致一个资源信息被多个用户同时拿到
Thread.sleep(10);
num--;
} catch (InterruptedException e) {
e.printStackTrace();
} finally{
lock.unlock();
}
}
}
}
//Thread.currentThread().getName() 拿到当前线程的引用和名称
public class ImplementDemo {
public static void main(String[] args) {
Apple a=new Apple();
new Thread(a,"A").start();
new Thread(a,"B").start();
new Thread(a,"C").start();
}
}