在使用synchronized方法时,我总结了使用的情形:
第一种情形:
/**
* Created by zhangzheming on 2018/1/12.
*/
public class SyncDubbo1 {
public synchronized void method1(){
System.out.println("method1..");
method2();
}
public synchronized void method2() {
System.out.println("method2..");
method3();
}
public synchronized void method3() {
System.out.println("method3");
}
public static void main(String[] args){
final SyncDubbo1 sd = new SyncDubbo1();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
sd.method1();
}
});
t1.start();
}
}
从上面的运行结果可以看出:在嵌套方法中使用synchronized,多线程情况下是线程安全的。
第二种情况:
/**
* Created by zhangzheming on 2018/1/12.
*/
public class syncDubbo2 {
// 主类
static class Main{
public int i = 10;
public synchronized void operationSup(){
try {
i--;
System.out.println("Main print i= "+i);
Thread.sleep(100);
}catch (Exception e){
e.printStackTrace();
}
}
}
// 子类
static class Sub extends Main{
public synchronized void operationSub(){
try{
while (i>0){
i--;
System.out.println("Sub print i= "+i);
Thread.sleep(100);
this.operationSup();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
public static void main(String[] args){
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
Sub sub = new Sub();
sub.operationSub();
}
});
t1.start();
}
}
此种情形主要是说明两点:
(1)在出现父子类继承关系的情况下,父类的成员变量也可以被子类继承。
(2)在父类和子类都使用synchronized ,出现多线程情况下,是线程安全的。
synchronized 的使用出现异常情况下,如果遇到异常,我们也需要处理异常情况,请看下面的代码:
/**
* Created by zhangzheming on 2018/1/12.
*/
public class SyncException {
private int i = 0;
public synchronized void operation(){
while (true){
try{
i++;
Thread.sleep(200);
System.out.println(Thread.currentThread().getName()+", i=" + i);
if (i%10 == 0){
Integer.parseInt("a");
}
}catch (InterruptedException e){
e.printStackTrace();
// (3) contiune;
//(2)System.out.println("log info i = "+i);
//(1)throw new RuntimeException();
}
}
}
public static void main(String[] args){
final SyncException se = new SyncException();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
se.operation();
}
},"t1");
t1.start();
}
}
这段代码其实将输出变量i的累加值,但是每逢遇到被10整除的数时,就会遇到异常现象,这种情况下,我们有三种解决办法:
(1)第一种办法:throw new RuntimeException(); 将运行异常抛出,这样程序就会停止下来,不会影响到后面的任务,比较适合任务之前有先后顺序的关联关系;
(2)第二种办法:System.out.println("log info i = "+i);将错误信息打印出来或者打印到日志中出,事后进行分析和修复。
(3)第三种办法:contiune; 那就是最坏的情况,忽略错误,不处理异常情况
不要使用String字符串加锁,这样会导致死循环产生
请看下面的例子:
package Thread;
/**
* Created by zhangzheming on 2018/1/15.
*/
public class StringLock {
public void method(){
// 使用字符串当做对象
synchronized ("字符串常量"){
try{
while (true){
System.out.println("当前线程:"+Thread.currentThread().getName()+"开始");
Thread.sleep(1000);
System.out.println("当前线程:"+Thread.currentThread().getName()+"结束");
}
}catch (Exception e){
e.printStackTrace();
}
}
}
public static void main(String[] args){
final StringLock stringLock = new StringLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
});
t1.start();
t2.start();
}
}
按照我们预想的,启动两个线程会在执行结果中产生两个线程交替进行,但是与实际结果不相同,这是为什么?
原因在于字符串属于引用类型,不管什么样的字符串都是一个类型对象,这样就导致了只有一个线程存在,回头想想其实要解决这个问题,就是满足synchronized 锁住的是对象就可以了,修改如下:
package Thread;
/**
* Created by zhangzheming on 2018/1/15.
*/
public class StringLock {
public void method(){
// 使用字符串当做对象
synchronized (new String("字符串常量")){
try{
while (true){
System.out.println("当前线程:"+Thread.currentThread().getName()+"开始");
Thread.sleep(1000);
System.out.println("当前线程:"+Thread.currentThread().getName()+"结束");
}
}catch (Exception e){
e.printStackTrace();
}
}
}
public static void main(String[] args){
final StringLock stringLock = new StringLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
});
t1.start();
t2.start();
}
}