之前对线程进行过简单介绍,现在更进一步介绍一下。
在创建线程使用第二种方法,即给Thread()类传入一个实现了Runnable接口的对象时,可能创建的两个线程用的是同一个对象,就存在共享资源。两个线程同时对其进行操作,而且顺序不定,存在安全问题。例:
class Resource implements Runnable {
public int i;
public Resource(int _i){
i = _i;
}
public void run(){
while(true){
if (i>0){
i--;
System.out.println(Thread.currentThread().getName()+" "+i);
}
else
break;
}
}
}
public class TestThread{
public static void main(String[] args){
Resource m = new Resource(100);
Thread t1 = new Thread(m);
Thread t2 = new Thread(m);
t1.start();
t2.start();
}
}
在这个例子中,线程t1和t2同时访问m的资源,都执行m对应对象的run()方法,即对i进行自减。但是两个线程的执行顺序不能确定,因此执行的结果不能确定,可能同时会进行对i的操作,安全性存在很大问题。举个形象的例子,一对夫妇,账户里有1000元,他们各自为这个账户办理了一张银行卡,相约一起取钱,两人同时按下取钱的按钮,如果线程不安全则两人会取走2000元,显然这是不合理的。
解决线程安全问题,java引入了监视器(monitor)来保证共享数据操作的同步性,任何对象都可以作为一个监视器,关键字synchronized修饰某个对象后,该对象就成为监视器。synchronized最常用的是以下两种方法:1.synchronized代码块:监视器就是指定的对象;2.synchronized方法:监视器就是this对象。
例:代码块方法:
class Resource implements Runnable {
volatile public int i;
volatile public Integer it;
public Resource(int _i){
i = _i;
it = new Integer(i);
}
public void run(){
while(true){
synchronized(it){
if (i>0)
{
try{
Thread.sleep(200);
}
catch(Exception e){}
i--;
System.out.println(Thread.currentThread().getName()+" "+i);
}
else{
System.out.println(Thread.currentThread().getName());
break;
}
}
}
}
}
public class TestSecurity{
public static void main(String[] args){
Resource m = new Resource(100);
Thread t1 = new Thread(m);
Thread t2 = new Thread(m);
t1.start();
t2.start();
}
}
具体不同就是标红的地方,给Resourse类新增加了一个Integer类的对象it,然后对其初始化,在run()方法中用synchronized方法块将if判断语句等包起来,监视器是it对象,也就是m对应的对象。在一个线程抢到执行权之后,只有这个线程执行完方法快包围的语句之后才能两个线程重新抢锁即执行权。
例:方法:
class Resource implements Runnable {
volatile public int i;
public Resource(int _i){
i = _i;
}
public synchronized void run(){
while(true){
if (i>0)
{
try{
Thread.sleep(200);
}
catch(Exception e){}
i--;
System.out.println(Thread.currentThread().getName()+" "+i);
}
else{
System.out.println(Thread.currentThread().getName());
break;
}
}
}
}
public class TestSecurity{
public static void main(String[] args){
Resource m = new Resource(100);
Thread t1 = new Thread(m);
Thread t2 = new Thread(m);
t1.start();
t2.start();
}
}
方法为标红部分,监视器的对象是this即当前对象m。线程的执行情况同第一种,也是先抢锁,抢到的线程执行方法里的内容,只有将方法全部执行完,两个线程开始重新抢锁。