本公众专注于为广大Java初学者和爱好者以及热爱编程的菜鸟提供学习交流平台,共同成长。 如果您发现什么错误、遗漏或不明的地方,可以通过文章下留言、群内讨论或私撩小编提出意见反馈,当然小编会给这些奉献的朋友分享Java 大型互联网项目实战视频资料(不定期更新视频资料) ,希望与大家一同成长!!!
我们再谈 synchronied同步,现在我们都知道了synchronied是同步代码了,它默认是的对象锁是this,往往效率都不高,因为每次执行到synchronized代码块时,其它的synchronized代码块都会被阻塞,那么怎么能提高效率呢?当然能实现同步就可以实现异步了。
前面一直使用的synchronied(this)锁,但是这样会有让效率很低,因为当线程运行到这个对象锁,就会阻塞这个对象下的其它所有的锁了,所以这样将会让运行效率大大降低,可以弄一个异步处理一段代码块,处理方法就是不要使用对象监视器(this)了,可以在同步代码块的前面定义一个临时对象Object obj = new Object();然后使用synchronied(obj),那么这样就是锁的对象监视器不同了,也就达到异步的效果了。这样效率也大大提高了。
看个例子。
public class TestThread6 {
public static void main(String[] args) throws InterruptedException {
ThreadEntity threadEntity = new ThreadEntity();
Runnable myRunnable1 = new MyRunnable(threadEntity,'小明','男');
Thread thread1 = new Thread(myRunnable1);
thread1.setName('A');
Runnable myRunnable2 = new MyRunnable(threadEntity,'小花','女');
Thread thread2 = new Thread(myRunnable2);
thread2.setName('B');
thread1.start();
thread2.start();
}
}
class MyRunnable implements Runnable {
private ThreadEntity threadEntity;
private String name;
private String sex;
public MyRunnable(ThreadEntity threadEntity,String name,String sex) {
this.threadEntity = threadEntity;
this.name = name;
this.sex = sex;
}
@Override
public void run() {
this.threadEntity.show(this.name,this.sex);
}
}
class ThreadEntity {
/**
* 大家都知道局部变量是不会出现脏读的
* @param name
* @param sex
*/
public void show(String name,String sex) {
try {
/**
* 实现异步处理就需要同步代码块使用各自的对象锁就可以了
*/
Object obj = new Object();
synchronized (obj) {
System.out.println(Thread.currentThread().getName()+',开始时间'+System.currentTimeMillis() +
',name=' + name + ',sex=' + sex);
//为了演示效果这里休眠2秒
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+',结束时间'+System.currentTimeMillis() +
',name=' + name + ',sex=' + sex);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
A,开始时间1524296126538,name=小明,sex=男
B,开始时间1524296126549,name=小花,sex=女
A,结束时间1524296128715,name=小明,sex=男
B,结束时间1524296128725,name=小花,sex=女
synchronied加到非静态方法上使用和静态方法上的使用方式类似,效果也是一样的,但是本质上是不同的,因为synchronied加到static方法上是给Class类上加锁了,而非静态方法上加锁是给对象加锁,
下面用一个小例子验证一下非静态方法和静态方法使用锁,并非是同一个锁 。为了看出效果,我们需要使用2个静态方法和一个非静态方法,使用3个线程来执行。
public class TestThread601 {
public static void main(String[] args) throws InterruptedException {
//共享类
ThreadEntity601 threadEntity601 = new ThreadEntity601();
//执行第一个静态方法的线程
Runnable myRunnable1 = new MyRunnable601(threadEntity601,'printA');
Thread thread1 = new Thread(myRunnable1);
thread1.setName('A...');
//执行第二个静态方法的线程
Runnable myRunnable2 = new MyRunnable601(threadEntity601,'printB');
Thread thread2 = new Thread(myRunnable2);
thread2.setName('B...');
//执行第三个非静态方法的线程
Runnable myRunnable3 = new MyRunnable601(threadEntity601,'printC');
Thread thread3 = new Thread(myRunnable3);
thread3.setName('C...');
thread1.start();
thread2.start();
thread3.start();
}
}
class MyRunnable601 implements Runnable {
private String flag;
private ThreadEntity601 threadEntity601;
public MyRunnable601(ThreadEntity601 threadEntity601,String flag) {
this.flag = flag;
this.threadEntity601 = threadEntity601;
}
@Override
public void run() {
if (this.flag == 'printA') {
this.threadEntity601.printA();
} else if (this.flag == 'printB') {
this.threadEntity601.printB();
} else {
this.threadEntity601.printC();
}
}
}
class ThreadEntity601 {
//第一个静态方法synchronized
public synchronized static void printA() {
try {
System.out.println('线程名称为:' + Thread.currentThread().getName()
+ '在 ' + System.currentTimeMillis() + '进入printAA');
//为了演示效果在此处加一个休眠3秒钟
Thread.sleep(3000);
System.out.println('线程名称为:' + Thread.currentThread().getName()
+ ' 在 ' + System.currentTimeMillis() + '离开printA');
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//第二个静态方法synchronized
public synchronized static void printB() {
System.out.println('线程名称为:' + Thread.currentThread().getName() + '在'
+ System.currentTimeMillis() + '进入printB');
System.out.println('线程名称为:' + Thread.currentThread().getName() + '在'
+ System.currentTimeMillis() + '离开printB');
}
//第三个非静态方法synchronized
public synchronized void printC() {
System.out.println('线程名称为:' + Thread.currentThread().getName() + '在'
+ System.currentTimeMillis() + '进入printC');
System.out.println('线程名称为:' + Thread.currentThread().getName() + '在'
+ System.currentTimeMillis() + '离开printC');
}
}
//结果显示效果和异步很像
线程名称为:C...在1524303493953进入printC
线程名称为:A...在 1524303493950进入printAA
线程名称为:C...在1524303494083离开printC
线程名称为:A... 在 1524303497087离开printA
线程名称为:B...在1524303497089进入printB
线程名称为:B...在1524303497091离开printB
以上结果显示成异步的效果的原因,是它们持有的不是同一把锁,printC持有的是一把对象锁,printA,printB持有的是同一把锁(Class锁),Class锁可以对该类的所有Class锁都有效。
synchronied(Class)代码块锁也是Class锁。它与synchronied static方法的作用一致,也是对整个类加锁。
改造刚刚那个printA,printB,printC,方法。
//第三个非静态方法synchronized
public static void printC() {
synchronized (ThreadEntity601.class) {
System.out.println('线程名称为:' + Thread.currentThread().getName() + '在'
+ System.currentTimeMillis() + '进入printC');
System.out.println('线程名称为:' + Thread.currentThread().getName() + '在'
+ System.currentTimeMillis() + '离开printC');
}
}
结果:
线程名称为:A...在 1524305002343进入printAA
线程名称为:A... 在 1524305005480离开printA
线程名称为:C...在1524305005483进入printC
线程名称为:C...在1524305005484离开printC
线程名称为:B...在1524305005486进入printB
线程名称为:B...在1524305005488离开printB
这样就完全实现同步了。
创建一个ThreadEntity601类,类中定一个定义2个方法,2个printA,printB同步方法,在printA方法中定一个死循环,printB中打印2句话。然后创建2个线程执行不同的方法。先启动调用printA方法的线程,那么就会形成无线等待了,看代码!
public class TestThread601 {
public static void main(String[] args) throws InterruptedException {
//共享类
ThreadEntity601 threadEntity601 = new ThreadEntity601();
//执行第一个静态方法的线程
Runnable myRunnable1 = new MyRunnable601(threadEntity601,'printA');
Thread thread1 = new Thread(myRunnable1);
thread1.setName('A...');
//执行第二个静态方法的线程
Runnable myRunnable2 = new MyRunnable601(threadEntity601,'printB');
Thread thread2 = new Thread(myRunnable2);
thread2.setName('B...');
thread1.start();
thread2.start();
}
}
class MyRunnable601 implements Runnable {
private String flag;
private ThreadEntity601 threadEntity601;
public MyRunnable601(ThreadEntity601 threadEntity601,String flag) {
this.flag = flag;
this.threadEntity601 = threadEntity601;
}
@Override
public void run() {
if (this.flag == 'printA') {
try {
this.threadEntity601.printA();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else if (this.flag == 'printB') {
this.threadEntity601.printB();
}
}
}
class ThreadEntity601 {
public synchronized void printA() throws InterruptedException {
System.out.println('线程名称为:' + Thread.currentThread().getName() + '进入printA');
boolean flag = true;
while (flag) {
//这里休眠一下让打印执行减慢
Thread.sleep(2000);
System.out.println('死循环中。。。');
}
System.out.println('线程名称为:' + Thread.currentThread().getName() + '离开printA');
}
public synchronized void printB() {
System.out.println('线程名称为:' + Thread.currentThread().getName() + '进入printB');
System.out.println('线程名称为:' + Thread.currentThread().getName() + '离开printB');
}
}
结果:
线程名称为:A...进入printA
死循环中。。。
死循环中。。。
死循环中。。。
死循环中。。。
这种情况下,A线程一直在执行,B线程无法拿到锁就一直,这样就锁死了,现在为了解决这个问题,可以使用同步代码块synchronied()来解决。
改造printB方法。
public void printB() {
Object object = new Object();
synchronized (object) {
System.out.println('线程名称为:' + Thread.currentThread().getName() + '进入printB');
System.out.println('线程名称为:' + Thread.currentThread().getName() + '离开printB');
}
}
线程名称为:B...进入printB
线程名称为:A...进入printA
线程名称为:B...离开printB
死循环中。。。
看看上面的结果是使用了不同的对象锁解决了这个问题。printB方法都打印出来了。
在开发中有当一个线程永远地持有一个锁,并且其它线程都尝试去获得这个锁时,那么它们将永远被阻塞,这个我们都知道。如果线程A持有锁L并且想获得锁M,线程B持有锁M并且想获得锁L,那么这两个线程将永远等待下去,这种情况就是最简单的死锁形式。
当一组Java线程发生死锁时,这两个线程就永远不能再使用了,并且由于两个线程分别持有了两个锁,那么这两段同步代码/代码块也无法再运行了----除非终止并重启应用。不过死锁造成的影响很少会立即显现出来,一个类可能发生死锁,并不意味着每次都会发生死锁,这只是表示有可能。当死锁出现时,往往是在最糟糕的情况----高负载的情况下。
比如2个人一起吃饭但是只有一双筷子,2人轮流吃(同时拥有2只筷子才能吃)。某一个时候,一个拿了左筷子,一人拿了右筷子,2个人都同时占用一个资源,等待另一个资源,这个时候甲在等待乙吃完并释放它占有的筷子,同理,乙也在等待甲吃完并释放它占有的筷子,这样就陷入了一个死循环,谁也无法继续吃饭。。。
再比如:某计算机系统中只有一台打印机和一台输入设备,进程P1正占用输入设备,同时又提出使用打印机的请求,但此时打印机正被进程P2 所占用,而P2在未释放打印机之前,又提出请求使用正被P1占用着的输入设备。这样两个进程相互无休止地等待下去,均无法继续执行,此时两个进程陷入死锁状态。
写一个简单的例子吧。
public class TestThread601 {
public static void main(String[] args) throws InterruptedException {
//共享类
ThreadEntity601 threadEntity601 = new ThreadEntity601();
//执行第一个静态方法的线程
Runnable myRunnable1 = new MyRunnable601(threadEntity601,'method1');
Thread thread1 = new Thread(myRunnable1);
//执行第二个静态方法的线程
Runnable myRunnable2 = new MyRunnable601(threadEntity601,'method2');
Thread thread2 = new Thread(myRunnable2);
thread1.start();
thread2.start();
}
}
class MyRunnable601 implements Runnable {
private String flag;
private ThreadEntity601 threadEntity601;
public MyRunnable601(ThreadEntity601 threadEntity601,String flag) {
this.flag = flag;
this.threadEntity601 = threadEntity601;
}
@Override
public void run() {
if (this.flag == 'method1') {
try {
this.threadEntity601.method1();
} catch (Exception e) {
e.printStackTrace();
}
} else if (this.flag == 'method2') {
try {
this.threadEntity601.method2();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class ThreadEntity601 {
private final Object lock1 = new Object() ;
private final Object lock2 = new Object() ;
public void method1() throws Exception {
synchronized (lock1) {
System.out.println('mothod1执行了lock1');
//为了演示效果明显这里休眠2秒
Thread.sleep(2000) ;
//这里拿到了method2方法中的lock2对象锁
synchronized (lock2) {
System.out.println('mothod1执行了lock2') ;
}
}
}
public void method2() throws Exception {
synchronized (lock2) {
System.out.println('mothod2执行了lock2');
//为了演示效果明显这里休眠2秒
Thread.sleep(2000) ;
//这里拿到了method1方法中的lock1对象锁
synchronized (lock1) {
System.out.println('mothod2执行了lock1') ;
}
}
}
}
Java之多线程(一)
联系客服