java多线程学习(一)

这块一直是薄弱的地方,只有模糊的概念,没有实战的经验~借此学习学习~


实现线程的两种方式:

1.继承java.lang.Thread,并重写它的run()方法,将线程的执行主体放入其中;

2.实现java.lang.Runnable接口,实现它的run()方法,将线程的执行主体放入其中。


线程类有一个Runnable类型的target属性,它是线程启动后要执行的run()方法所属主体,如果采用的是继承Thread类的方式,那么这个target就是线程对象自身;如果采用的是实现Runnable接口的方式,那么这个target就是实现了Runnable接口的类的实例。


接下来说同步

同步的使用可以保证在多线程运行的环境中,程序不会产生设计之外的错误结果。同步的实现方式有两种,同步方法同步块,都要用到synchronized关键字。

*同步方法

给一个方法加synchronized修饰符之后就可以使它成为同步方法,这个方法可以是静态方法和非静态方法。

先看一段代码:

public class ThreadTest1 extends Thread {
	private int threadNo;
	public ThreadTest1(int threadNo) {
		this.threadNo = threadNo;
	}
	
	public synchronized void run() {
		for(int i=0;i<100;i++){
			System.out.println("线程"+threadNo+":"+i);
		}
	}
	
	public static void main(String[] args)throws Exception {
		for(int j=1;j<10;j++){
			new ThreadTest1(j).start();
			Thread.sleep(1);
		}
	}

}

这个程序其实就是让10个线程在控制台上数数,从0~99。理想情况下,我们希望看到一个线程数完,然后才是另一个线程开始数数。这是当你执行了这个程序之后就会发现,这些线程并没有按照我们的期望执行,是没有规律的抢着报数。

也许你会说:怎么会这样?!不是已经在run()方法前加synchronized关键字了么,按理来说,这些线程应该可以一个接一个的执行run()方法啊?!


对于一个非静态成员方法加synchronized关键字,这实际上是以这个成员方法所在的对象本身作为对象锁。在这个例子中,就是以ThreadTest1类的一个具体对象,也就是该线程自身作为对象锁的。一共十个线程,每个线程持有自己线程对象的那个对象锁,这必然不能产生同步的效果。


再看这段代码:

public class ThreadTest2 extends Thread {
	private int threadNo;
	public ThreadTest2(int threadNo) {
		this.threadNo = threadNo;
	}
	
	public void run() {
		abc(threadNo);
	}
	
	public static synchronized void abc(int threadNo) {
		for(int i=0;i<100;i++){
			System.out.println("线程"+threadNo+":"+i);
		}
	}
	
	public static void main(String[] args)throws Exception {
		for(int j=1;j<10;j++){
			new ThreadTest2(j).start();
			Thread.sleep(1);
		}
	}
}

这个代码是run()方法调用一个静态的同步方法abc(),运行该程序你会发现一个线程数完0~99另一个线程才开始。

对于同步静态方法,对象锁就是该方法所在类的Class实例,在JVM中,所有被加载的类都有唯一的类对象,具体到本例,就是唯一的ThreadTest2.class对象。不管我们创建了该类的多少个实例,但是它的类实例就一个。

一但一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象。

 

 

再来看一段代码:

public class ThreadTest3 extends Thread {
	private int threadNo;
	private String lock;
	
	public ThreadTest3(int threadNo,String lock) {
		this.threadNo = threadNo;
		this.lock = lock;
	}
	
	public void run() {
		synchronized(lock){
			for(int i=0;i<100;i++){
				System.out.println("线程"+threadNo+":"+i);
			}
		}
	}
	
	public static void main(String[]args)throws Exception{
		String lock = new String("lock");
		for(int j=1;j<10;j++){
			new ThreadTest3(j,lock).start();
			Thread.sleep(1);
		}
	}
}


我们注意到,该程序通过在main()方法启动10个线程之前,创建了一个String类型的对象。并通过ThreadTest3的构造函数,将这个对象赋值给每一个ThreadTest3线程对象中的私有变量lock,这些线程的lock变量实际上指向的都是堆内存中的同一区域,即存放main函数中的lock变量的区域。run()方法里的synchronized块,这个同步块的对象锁,就是main方法中创建的那个String对象。指向的是同一个String类型的对象,对象锁是共享且唯一的!









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值