Java线程同步
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
开发工具与关键技术:
作者:陈剑波
撰写时间:2020年06月08日
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
线程安全问题
线程安全是多线程编程中经常需要考虑的一个问题,线程安全是指多线程环境下多个线程可能会同时对同一段代码或者共享变量进行执行,如果每次运行的结果和单线程下的结果是一致的,那么就是线程安全的,如果每次运行的结果不一致,那么就是线程不安全的。
线程安全测试:
public class TraditionalThreadSynchronized { public static void main(String[] args) { final Outputter output = new Outputter(); new Thread() { public void run() { output.output("zhangsan"); } }.start(); new Thread() { public void run() { output.output("lisi"); } }.start(); } } class Outputter { public void output(String name) { // TODO 为了保证对name的输出不是一个原子操作,这里逐个输出name的每个字符 for(int i = 0; i < name.length(); i++) { System.out.print(name.charAt(i)); // sleep()方法让当前线程睡眠,睡眠一段时间后重新获取到cpu的使用权。 Thread.sleep(10); } } } |
运行结果:
|
输出的字符串被打乱了,我们期望的输出结果是zhangsanlisi。这就是线程同步问题,我们希望output方法被一个线程完整的执行完之后再切换到下一个线程。
使用同步代码块解决线程安全问题
同步代码块使用synchronized(object){}进行修饰,在调用该代码块时,需要获得内置锁,否则就处于阻塞状态。
代码格式如下:
synchronized(object){ //内容 } |
示例代码:
public class TraditionalThreadSynchronized { public static void main(String[] args) { final Outputter outputer = new Outputter(); new Thread() { @Override public void run() { // TODO Auto-generated method stub synchronized (outputer) { try { outputer.output("zhangsan"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }.start(); new Thread() { @Override public void run() { // TODO Auto-generated method stub synchronized (outputer) { try { outputer.output("lisi"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }.start(); } } |
运行结果:
zhangsanlisi |
使用同步方法解决线程安全问题
同步方法使用synchronized修饰方法,在调用该方法前,需要获得内置锁(java每个对象都有一个内置锁),否则就处于阻塞状态。
代码格式如下:
public synchronized void output(){//内容} |
示例代码:
public class Outputter { public synchronized void output(String name) throws InterruptedException { for (int i = 0; i < name.length(); i++) { System.out.print(name.charAt(i)); Thread.sleep(10); } } } |
运行结果:
zhangsanlisi |
使用同步锁(Lock)解决线程安全问题
Lock是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占 访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得 Lock对象。
在实现线程安全的控制中,比较常用的是 ReentrantLock(可重入锁)。使用该Lock 对象可以显式地加锁、释放锁。
代码格式如下:
//定义锁对象 private final ReentrantLock lock=new ReentrantLock(); //需要保证线程安全的方法 public void methodName(){ //加锁 lock.lock(); try{ //代码块 } //使用finally块来保证一定可以释放锁 finally{ //释放锁 lock.unlock(); } } |
示例代码:
public class Outputter { private final ReentrantLock lock = new ReentrantLock();
public void output(String name) throws InterruptedException { lock.lock(); try { for (int i = 0; i < name.length(); i++) { System.out.print(name.charAt(i)); Thread.sleep(10); } } finally { lock.unlock(); } } }
|
运行结果:
zhangsanlisi |