很长时间没有更新博客,恰好这段时间工作上需要使用线程,就稍微花点时间再次复习(学习)了一下线程知识。在此文中我将围绕以下几点对线程进行讲解:
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
五、以上规则对其它对象锁同样适用:
public class Thread3 { class Inner { private void m4t1() { int i = 5; while(i-- > 0) { System.out.println(Thread.currentThread().getName() + " : Inner.m4t1()=" + i); try { Thread.sleep(500); } catch(InterruptedException ie) { } } } private void m4t2() { int i = 5; while(i-- > 0) { System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i); try { Thread.sleep(500); } catch(InterruptedException ie) { } } } } private void m4t1(Inner inner) { synchronized(inner) { //使用对象锁 inner.m4t1(); } private void m4t2(Inner inner) { inner.m4t2(); } public static void main(String[] args) { final Thread3 myt3 = new Thread3(); final Inner inner = myt3.new Inner(); Thread t1 = new Thread( new Runnable() {public void run() { myt3.m4t1(inner);} }, "t1"); Thread t2 = new Thread( new Runnable() {public void run() { myt3.m4t2(inner);} }, "t2"); t1.start(); t2.start(); } }
关键字synchronizedde的锁都是对象锁,而不是扒一段代码(方法)当做锁,所以示例代码中哪个线程先执行带有synchronized关键字的方法,那个线程就执有该方法所述对象的锁,不同对象获得的就是不同对象的锁,它们之间互不影响,有一种情况是相同的,那就是在静态方法上加synchronized关键字,表示锁定class类,类级别的锁,可以叫类锁,在使用的时候注意区分。
synchronized部分借鉴:http://www.wgblogs.com/ 在此表示感谢