创建线程和synchronized关键字
很长时间没有更新博客,恰好这段时间工作上需要使用线程,就稍微花点时间再次复习(学习)了一下线程知识。在此文中我将围绕以下几点对线程进行讲解:
1.线程的创建(thread,runnable)
2.Synchronized关键字(生产者消费者)
一、线程创建
相信学习过java的同学都知道,创建线程无非是两种方式:
(1)继承Thread类;
(2)实现Runnable接口;
那这两种方式都有什么区别呢?就实现方式来看还是有差别的,继承Thread类就需要去重写Thread父类中的run方法,run方法就是执行的公共代码块;而实现Runnable则是对接口中run方法的具体实现,两种方式的run方法的作用相同。下面一起分析一下代码:
package com.cct.thread; class MyThread extends Thread{ private int ticket = 10; public void run(){ for(int i =0;i<500;i++){ if(this.ticket>0){ System.out.println(Thread.currentThread().getName()+"在卖票---->"+(this.ticket--)); } } } } (1)测试一 public static void main(String[] args) { MyThread mt1 = new MyThread(); MyThread mt2 = new MyThread(); MyThread mt3 = new MyThread(); //创建了三个对象 mt1.start(); mt2.start(); mt3.start(); } (2)测试二 public static void main(String[] args) { MyThread thr=new MyThread(); Thread a=new Thread(thr,"1号"); Thread b=new Thread(thr,"2号"); Thread c=new Thread(thr,"3号"); //一个对象用三个线程调用 a.start(); b.start(); c.start(); }
同学们应该可以猜到看出上面的两种测试方式的结果差别,第一个main会给三个线程都分配10张票进行售卖,而第二种则是共用10张票进行售卖,在网上看到很多人描述继承Thread和实现Runnable接口的差别是是否共同消耗同一份资源,我觉得这种说法是错误的,不管是哪种方式实现线程都是可以实现,这点要注意。他们的差别在于除了在类上写的方式有点不一样之外,我觉着比较大的差别是, java只支持但继承,但是可以多实现,所以我认为实现runnable比继承Thread扩展性更强。
二、synchronized关键字
这是使用线程最常用的关键字之一,它的作用是在同一时间点只有一个线程获得锁并进入执行代码,其他线程如也需要获得这把锁,则须要在外等待,直到这个线程执行完。
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
话不多说,先来代码进行举例:
一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
public class Thread1 implements Runnable {
public void run() {
synchronized(this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);
}
}
}
public static void main(String[] args) {
Thread1 t1 = new Thread1();
Thread ta = new Thread(t1, "A");
Thread tb = new Thread(t1, "B");
ta.start();
tb.start();
}
}
结果:
A synchronized loop 0
A synchronized loop 1
A synchronized loop 2
A synchronized loop 3
A synchronized loop 4
B synchronized loop 0
B synchronized loop 1
B synchronized loop 2
B synchronized loop 3
B synchronized loop 4
二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
public class Thread2 {
public void m4t1() {
synchronized(this) {
int i = 5;
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
}
public void m4t2() {
int i = 5;
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
public static void main(String[] args) {
final Thread2 myt2 = new Thread2();
Thread t1 = new Thread( new Runnable() { public void run() { myt2.m4t1(); } }, "t1" );
Thread t2 = new Thread( new Runnable() { public void run() { myt2.m4t2(); } }, "t2" );
t1.start();
t2.start();
}
}
结果:
t1 : 4
t2 : 4
t1 : 3
t2 : 3
t1 : 2
t2 : 2
t1 : 1
t2 : 1
t1 : 0
t2 : 0
三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
//修改Thread2.m4t2()方法: public void m4t2() { synchronized(this) { int i = 5; while( i-- > 0) { System.out.println(Thread.currentThread().getName() + " : " + i); try { Thread.sleep(500); } catch (InterruptedException ie) { } } } } 结果: t1 : 4 t1 : 3 t1 : 2 t1 : 1 t1 : 0 t2 : 4 t2 : 3 t2 : 2 t2 : 1 t2 : 0
四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
//修改Thread2.m4t2()方法如下: public synchronized void m4t2() { int i = 5; while( i-- > 0) { System.out.println(Thread.currentThread().getName() + " : " + i); try { Thread.sleep(500); } catch (InterruptedException ie) { } } } 结果: t1 : 4 t1 : 3 t1 : 2 t1 : 1 t1 : 0 t2 : 4 t2 : 3 t2 : 2 t2 : 1 t2 : 0
synchronized(this)里的this 是指当前class的实例,即对象;
关键字synchronizedde的锁都是对象锁,而不是扒一段代码(方法)当做锁,所以示例代码中哪个线程先执行带有synchronized关键字的方法,那个线程就执有该方法所述对象的锁,不同对象获得的就是不同对象的锁,它们之间互不影响,有一种情况是相同的,那就是在静态方法上加synchronized关键字,表示锁定class类,类级别的锁,可以叫类锁,在使用的时候注意区分,上述代码描述了现象,但只是做了一些小总结,但是归结起来就是类所和对象锁的定义和使用区别。
synchronized
void
f() {
/* body */ }
和void f() { synchronized(this) { /* body */
} }
是完全等价的。
synchronized(class)很特别,它会让另一个线程在任何需要获取class做为monitor的地方等待。class与this作为不同的监视器可以同时使用,不存在一个线程获取了class,另一个线程就不能获取该class的一切实例。
- 对于实例同步方法,锁是当前实例对象。
- 对于静态同步方法,锁是当前对象的Class对象。
- 对于同步方法块,锁是Synchonized括号里配置的对象。
synchronized(class)
synchronized(this)
线程各自获取monitor,不会有等待。
synchronized(this)synchronized(this)
如果不同线程监视同一个实例对象,就会等待;如果不同的实例,不会等待。
synchronized(class)synchronized(class)
如果不同线程监视同一个实例或者不同的实例对象,都会等待。
(1)对象锁 private Object lock = new Object(); …synchronized(lock){} (2)类锁 private static Object lock = new Object(); …synchronized(lock){}
…synchronized(Class*){}
synchronized部分借鉴:http://www.wgblogs.com/ 在此表示感谢