class ThreadText implements Runnable {
private int num = 10;
private Object obj = new Object();
@Override
public void run() {
while (true) {
if (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(num--);
}
}
}
这段代码有线程安全问题,使用多线程时会发生数据错误。
原因:(也是我们以后判断一个程序是否有线程安全问题的依据)
a.线程任务中操作的是共享数据。
b.线程任务中操作共享数据的代码有多条(运行有多个)。
怎么解决?需要使用线程的同步机制。
class ThreadText implements Runnable {
private int num = 10;
private Object obj = new Object();
@Override
public void run() {
synchronized (obj) {
while (true) {
if (num > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(num--);
}
}
}
}
(1)线程的同步机制。
好比一个人上洗手间时,他进入洗手间会上锁,出来的时候再把锁打开,然后其他人才能进去。
(2)在java中提供了同步机制,可以有效的防治资源冲突,同步机制使用synchronized关键字。
有一个前提:多个线程使用一把锁,这个才叫线程同步。
(2)在java中提供了同步机制,可以有效的防治资源冲突,同步机制使用synchronized关键字。
有一个前提:多个线程使用一把锁,这个才叫线程同步。
(3)同步的弊端:
a.降低了程序的效率。
b.当线程任务(run方法)中出现了多个同步,如果同步中嵌套了其他的同步,这个时候就会容易发生死锁。
例如
synchronized(obj 1){
synchronized(obj 2){ }
}
synchronized(obj 2){
synchronized(obj 1){ }
}
4)同步机制解决的问题:
当多线程安全问题发生时,加入了同步后,就可以保证一个线程把所以的同步执行完,但是如果问题依旧,那么久要通过同步的
前提来判断同步是否正确。
注意:
如果当加上同步时,安全问题依旧,那么就要检查是否一个同步。
(5)同步块:
a.synchronized(obj){ } 这是一把锁
b.synchronized(new Object() ){ } 这样不是使用同一把锁 ,错误的写法。
通常将共享数据的操作放置在synchronized定义的区域内,这样当其他线程也获取到这个锁时,必须等待锁被释放时才能进入该区域。
(6)同步方法:就是在方法前面修饰synchronized关键字的方法。
例如
public synchronized void xd(){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(num--);
}
@Override
public void run() {
while (true) {
xd();
}
}
注意:同步方法也是有锁的,它的锁是this。
(7) 静态同步方法:使用的锁是字节码文件对象。 字节码文件对象——> 类名.class
总结
当多线程任务只需要一个同步时完全可以使用同步方法,
当多线程任务需要多个同步时,必须要通过锁来进行区分,这时候必须要使用同步代码,所以同步代码较为常用