线程同步方法

线程同步的方法

1、使用synchronized获取对象互斥锁:这个最常用的的也是比较安全的一种方式,采用synchronized修饰符实现的同步机制叫做互斥锁机制,它所获得的锁叫做互斥锁。

public class Test
  public static void main(String[] args) throws IOException  {
        
    	Test test = new Test();
        
        Test.MyThread thread1 = test.new MyThread();
        Test.MyThread thread2 = test.new MyThread();
        thread1.start();
        thread2.start();
    } 
}
class MyThread extends Thread{
        @Override
        public void run() {
            synchronized (object) {
                i++;
                System.out.println("i:"+i);
                try {
                    System.out.println("线程"+Thread.currentThread().getName()+"进入睡眠状态");
                    Thread.currentThread().sleep(10000);
                } catch (InterruptedException e) {
                    // TODO: handle exception
                }
                System.out.println("线程"+Thread.currentThread().getName()+"睡眠结束");
                i++;
                System.out.println("i:"+i);
            }
        }
    }

2、使用特殊域变量volatile实现线程同步:volatile修饰的变量是一种稍弱的同步机制,因为每个线程中的成员变量都会对这个对象私有拷贝,每个线程获取的数据都是从私有拷贝中获取到的。而当volatile修饰之后,代表这个对象只能从共享内存中获取,禁止私有拷贝。访问volatile变量时不会执行加锁操作,因此也不会执行线程阻塞。因此volatile变量是一种比synchronize关键字更轻量级的同步机制。但是因此会比使用加锁更加不安全,使用同步机制会更安全一些,当且仅当满足以下所有条件时,才应该使用volatile变量,1、对变量的写入操作不依赖当前值,或者能确保只有单个线程更新变量的值。该变量没有包含在具有其他变量的不变式中。


 class Bank {
            //需要同步的变量加上volatile
            private volatile int account = 100;
 
            public int getAccount() {
                return account;
            }
            //这里不再需要synchronized 
            public void save(int money) {
                account += money;
            }

3、使用重入锁Lock实现线程同步,这是在jdk5以后才导入的同步方式,因为synchronize同步之后会存在一个阻塞的过程,如果这个阻塞的时间过久,验证影响我们代码的质量以及系统系能上的问题,我们需要一种机制,让等待的线程到达一段时间以后能响应中断,这就是lock的作用。
在使用synchronize关键字实现同步的话,在遇到IO阻塞的情况时,其他线程就只能等待无法进行操作,这样十分影响效率。而Lock可以让等待的线程进入中断先去执行其他任务。
对于中断,Lock中的两种锁有不同的响应方式,lock方法会优先考虑获取锁,等到锁获取成功后才响应中断,而lockInterruptibly 优先考虑响应中断,而不是响应锁的普通获取或重入获取。

static Lock lock=new ReentrantLock();
 private static void do321() throws InterruptedException {
        lock.lock();
        System.out.println(Thread.currentThread().getName() + " is locked.");
        try {
            System.out.println(Thread.currentThread().getName() + " doSoming1....");
            Thread.sleep(10000);// 此时才会响应中断
            System.out.println(Thread.currentThread().getName() + " doSoming2....");

            System.out.println(Thread.currentThread().getName() + " is finished.");
        } finally {
            lock.unlock();
        }
    }
public static void main(String[] args) throws InterruptedException {
        Thread thread1=new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    System.out.println("Thread 1 get lock");
                    do321();
                    System.out.println("Thread 1 end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("thread 1 is interrupted!!!");
                }

            }
        });
        Thread thread2=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("Thread 2 get lock");
                    do321();
                    System.out.println("Thread 2 end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("thread 2 is interrupted!!!");
                }
            }
        });
        thread1.setName("Thread 1");
        thread2.setName("Thread 2");
        thread1.start();
        Thread.sleep(1000);
        thread2.start();
        Thread.sleep(1000);
        thread2.interrupt();
    }
    
    output:
    Thread 1 get lock
Thread 1 is locked.
Thread 1 doSoming1....
Thread 2 get lock
Thread 1 doSoming2....
Thread 1 is finished.
Thread 1 end
Thread 2 is locked.
Thread 2 doSoming1....
thread 2 is interrupted!!!
java.lang.InterruptedException: sleep interrupted
	at java.base/java.lang.Thread.sleep(Native Method)
	at com.lock.test.Test.do321(Test.java:26)
	at com.lock.test.Test$2.run(Test.java:56)
	at java.base/java.lang.Thread.run(Thread.java:830)

从例子中可以看出使用了lock锁,当出现中断的时候Thread2线程不会马上响应中断,而是等Thread1释放锁,Thread2获取锁以后才响应的中断

static Lock lock=new ReentrantLock();
    private static void do123() throws InterruptedException {
        lock.lockInterruptibly();
        System.out.println(Thread.currentThread().getName() + " is locked.");
        try {
            System.out.println(Thread.currentThread().getName() + " doSoming1....");
            Thread.sleep(10000);// 等待几秒方便查看线程的先后顺序
            System.out.println(Thread.currentThread().getName() + " doSoming2....");

            System.out.println(Thread.currentThread().getName() + " is finished.");
        } finally {
            lock.unlock();
        }
    }
public static void main(String[] args) throws InterruptedException {
        Thread thread1=new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    System.out.println("Thread 1 get lock");
                    do123();
                    System.out.println("Thread 1 end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("thread 1 is interrupted!!!");
                }

            }
        });
        Thread thread2=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("Thread 2 get lock");
                    do123();
                    System.out.println("Thread 2 end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("thread 2 is interrupted!!!");
                }
            }
        });
        thread1.setName("Thread 1");
        thread2.setName("Thread 2");
        thread1.start();
        Thread.sleep(1000);
        thread2.start();
        Thread.sleep(1000);
        thread2.interrupt();
    }
output:
Thread 1 get lock
Thread 1 is locked.
Thread 1 doSoming1....
Thread 2 get lock
java.lang.InterruptedException
	at java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:944)
	at java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1263)
	at java.base/java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:317)
	at com.lock.test.Test.do123(Test.java:9)
	at com.lock.test.Test$2.run(Test.java:56)
	at java.base/java.lang.Thread.run(Thread.java:830)
thread 2 is interrupted!!
Thread 1 doSoming2....
Thread 1 is finished.
Thread 1 end

从这个例子中可以看出lockInterruptibly锁,会立即响应中断,而是等获取锁以后在响应。
4、使用ThradLocal管理变量:如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。

ThreadLocal() : 创建一个线程本地变量 
get() : 返回此线程局部变量的当前线程副本中的值 
initialValue() : 返回此线程局部变量的当前线程的"初始值" 
set(T value) : 将此线程局部变量的当前线程副本中的值设置为value
public class MyThreadLocal {
    private static ThreadLocal<Object> threadLocal=new ThreadLocal<Object>(){
        @Override
        protected Object initialValue() {
            System.out.println("调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!");
            return 5;
        }
    };

    public static void main(String[] args) throws InterruptedException {
        Thread thread1=new Thread(new Runnable() {
            @Override
            public void run() {
                int nums= (int) threadLocal.get();
                while(nums>0) {
                    System.out.println("书a总数为:" + nums);
                    nums--;
                    threadLocal.set(nums);
                }
                nums=(int) threadLocal.get();
                System.out.println("书a剩余数量:"+nums);
            }
        });
        Thread thread2=new Thread(new Runnable() {
            @Override
            public void run() {
                int nums= (int) threadLocal.get();
                while(nums>0) {
                    System.out.println("书b总数为:" + nums);
                    nums--;
                    threadLocal.set(nums);
                }
                nums=(int) threadLocal.get();
                System.out.println("书b剩余数量:"+nums);
            }
        });
        thread1.start();
        Thread.sleep(100);
        thread2.start();
    }
}
output:
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
书a总数为:5
书a总数为:4
书a总数为:3
书a总数为:2
书a总数为:1
书a剩余数量:0
调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!
书b总数为:5
书b总数为:4
书b总数为:3
书b总数为:2
书b总数为:1
书b剩余数量:0

从例子中我们可以看出,虽然Thread1已经初始化了ThreadLocal里的值,但是当Thread2去获取的时候,任然重新初始化了,可以看出不同线程都会获取只属于自己的ThreadLocal。
ThreadLocal在对于那些线程不安全的工具类的使用中有很大的用处:例如SimpleDateFomat是线程不安全的,但是若每次使用都重新new的话又很繁琐,但是若使用ThreadLocal封装这个类就完全可以避免这个问题,阿里巴巴java开发手册是这么推荐的:

public class DateUtils {
    public static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyy-MM-dd");
        }
    };
}

当不同线程要使用SimpleDateFormat的时候,根据ThreadLocal的机制我们可以得知,第一次使用get方法的时候都会调用initialValue()方法,这样不同线程获取的就是不同的SimpleDateFormat对象,这样也就避免了线程不安全的问题了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值