线程安全问题出现的根本原因:
1.存在俩个或俩个以上的线程共享着一个资源.
2.对共享的资源有写的操作.
解决方案:
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程是不可以参与运算的.
必需要当前线程把这些都执行完毕之后,其他线程才能参与运算.
同步的好处:解决了线程的安全问题.
同步的弊端:相对降低了效率,因为同步外的线程都会判断同步锁
同步的前提:同步中必须有多个线程争取同一个锁.
public class Main {
static Object lock = new Object();
static class MyThread1 extends Thread{
@Override
public void run() {
//同步代码块
synchronized (lock){
for (int i = 0; i < 10000; i++) {
System.out.println("我是张三");
}
}
}
}
static class MyThread2 extends Thread{
@Override
public void run() {
synchronized (lock){
for (int i = 0; i < 10000; i++) {
System.out.println("我是李四");
}
}
}
}
public static void main(String[] args) {
//创建俩个线程对象
Thread t1 = new MyThread1();
t1.start();
Thread t2 = new MyThread2();
t2.start();
}
}
同步代码块的注意事项:
1.同步代码块的锁可以是任意的对象.同步函数的锁是固定的,非静态函数的锁对象是this对象.
2.锁对象必须是多线程共享的对象,否则锁不住.
3.在同步代码块或者是同步函数中调用sleepsleep方法是不会释放锁对象的,如果是调用了wait方法是会释放锁对象的.
2.同步函数:
public class Main {
static int r = 0;
static int count = 300000;
static class Add extends Thread{
@Override
public synchronized void run() {
for (int i = 0; i < count; i++) {
r++;
}
}
}
static class Sub extends Thread{
@Override
public synchronized void run() {
for (int i = 0; i < count; i++) {
r--;
}
}
}
public static void main(String[] args) throws InterruptedException {
Add add = new Add();
add.start();
Sub sub = new Sub();
sub.start();
add.join();
sub.join();
System.out.println(r);
}
}
1.如果函数是一个非静态的同步函数,那么锁对象是this对象;
2.如果函数是静态的同步函数,那么锁对象是当前函数所属的类的字节码文件(class对象);
3.同步函数的锁对象是固定的,不能由自己指定。
使用synchronized可以保证原子性,内存可见性,代码重排序,锁具有互斥性.
可以使用volatile关键字来保证变量前后代码的执行顺序