JAVA——多线程(2)

上一篇多线程java

文章摘要
对比继承和实现方法,线程安全与线程同步及其方法

对比继承Thread和实现Runable两种方法android

继承Thread

1.java中类是单继承的,若是继承Thread,该类就不能有其余父类了
2.从操做上看,继承方式更简单
3.但没法作到多线程共享同一资源web

实现Runable

1.java可实现多接口,该类还可继承其它父类
2.从操做上看,实现方法更复杂,获取线程名称也更复杂(Thread.currentThread())
3.能作到共享同一资源安全

解释一下共享同一资源
假设我有50个苹果,有A,B,C三我的同时吃苹果
继承:多线程

public class Xianc {
	    public static void main(String[] args) {
	    	 new eatapple("A").start();
			 new eatapple("B").start();
			 new eatapple("C").start();
		}
	}	
class eatapple extends Thread{
	 private int num = 50;
	 public eatapple(String name) {
		 super(name);
	 }
	 public void run() {
		 for(int i = 1;i<=50;i++) {
			 if(num>0) {
			 System.out.println(super.getName()+"吃了"+num--+"个苹果");//获取名称这与实现的不一样
			 }
		 }
	 }
}

实现并发

public class Xianc {
	    public static void main(String[] args) {
	    	 eatapple e = new eatapple();
	    	 new Thread(e,"A").start();
			 new Thread(e,"B").start();
			 new Thread(e,"C").start();
		}
	}	
class eatapple implements Runnable{
	 private int num = 50;
	 
	 public void run() {
		 for(int i = 1;i<=50;i++) {
			 if(num>0) {
				
			 System.out.println(Thread.currentThread().getName()+"吃了"+num--+"个苹果");
			 }
		 }
	 }
}

运行后的继承方法控制台输出的是ABC各吃了50个苹果
而使用实现接口方法,控制台输出的是ABC一共吃了50个苹果app

线程同步

线程不安全

当多线程并发的访问同一个资源对象时,可能出现线程不安全的状况
举例:svg

public class Xianc {
	    public static void main(String[] args) {
	    	 eatapple e = new eatapple();
	    	 new Thread(e,"A").start();
			 new Thread(e,"B").start();
			 new Thread(e,"C").start();
		}
	}	
class eatapple implements Runnable{
	 private int num = 50;
	 
	 public void run() {
		 for(int i = 1;i<=50;i++) {
			 if(num>0) {
			 try {
			 Thread.sleep(10);  //使线程不安全的现象更加明显,并非sleep()方法致使的线程不安全
			 }catch(InterruptedException e){
			 e.printStackTrace();
			 }
			 System.out.println(Thread.currentThread().getName()+"吃了"+num--+"个苹果");
			 }
		 }
	 }
}

运行后发现:(截取有表明性的一部分)
在这里插入图片描述
发现B和C同时吃了49号和48号的苹果。
缘由:上述代码每一个线程的操做分为两部分:
1.展现手上拿到的苹果编号
在这里插入图片描述
2.再吃掉苹果(苹果数减一)
在这里插入图片描述
而这两部分未能同步进行。
解决方法:
1.同步代码块:
2.同步方法:
3.锁机制:性能

同步代码块

格式:this

syncronized(同步锁) 
{
   须要同步的代码
}

代码以下:

public void run() {
		 for(int i = 1;i<=50;i++) {
			 synchronized (this) {
				 if(num>0) {
				 try {
				 Thread.sleep(10);
				 }catch(InterruptedException e){
				 e.printStackTrace();
				 }
				 System.out.println(Thread.currentThread().getName()+"吃了"+num--+"个苹果");
				 }
			 }
		 }
	 }
同步方法

(使用synchronized修饰的方法叫同步方法,保证A线程执行该方法时,其余线程只能在外边等着)
格式:synchronized public void doWork(){ //TODO }
代码以下

public void run() {                 //不能用synchronized修饰run方法
		 for(int i = 1;i<=50;i++) {
			 eat();
		 }
	 }
	 //定义一个用用synchronized修饰的新方法,并由run方法调用
		 synchronized private void eat() {
			 if(num>0) {
				 try {
				 Thread.sleep(10);
				 }catch(InterruptedException e){
				 e.printStackTrace();
				 }
				 System.out.println(Thread.currentThread().getName()+"吃了"+num--+"个苹果");
				 }
			 }
	 }

同步锁:
对于非static方法,同步锁是this;
对于static方法,同步锁是当前方法所在的字节码对象(Apple2.class)

使用synchronized的利弊:
好处:保证了多线程并发访问的同步操做,避免了线程的安全性问题
缺点:用synchronized修饰的代码块性能低
建议:尽可能减小synchronized的做用域

锁机制

Lock机制提供了比synchronized代码块和synchronized方法更普遍的锁定操做,上述两种方法具备的功能Lock都有,并更增强大,更能体现面向对象思想
格式:

class X{
	private final ReentrantLock lock = new ReentrantLock();
	//......
	public void m(){                  //m是须要同步的方法
		lock.lock();                  //实例(第一个lock)调用lock()方法
		try{
		}catch(){
		}finally{
			lock.unlock();             //必需要有的步骤,关闭锁!
		}
	}
}

代码以下

public class Xianc {
    public static void main(String[] args) {
		eatapple e = new eatapple();	
		new Thread(e,"小A").start();
		new Thread(e,"小B").start();
		new Thread(e,"小C").start();                     //经过实例化eatapple建立三个线程
	    }
}
class eatapple implements Runnable{
	 private int num = 50;                                //苹果数量
	 private final Lock l = new ReentrantLock();          //建立一个锁对象
	 public void run() {
		 for(int i = 1;i<=50;i++) {
			 eat();
	}
}
	 private void eat() {
		 l.lock();
			 try {
				 if(num>0) {
				 System.out.println(Thread.currentThread().getName()+"吃了"+num+"个苹果");
				 Thread.sleep(10);
				 num--;
				 }
			 }catch(InterruptedException e){
				 e.printStackTrace();
			 }finally {
				 l.unlock();
		 }	
	 }
}