从0开始学习多线程

不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,都是通过Thread的start()方法来运行的。

扩展java.lang.Thread 类。

位于java.lang包下的Thread类是非常重要的线程类,它实现了Runnable接口,还拥有许多自己的属性和方法。

在Thread类中,有一些比较关键的属性,比如name是表示Thread的名字,可以通过Thread类的构造器中的参数来指定线程名字,priority表示线程的优先级(最大值为10,最小值为1,默认值为5),daemon表示线程是否是守护线程(垃圾回收线程是称职的守护线程,并不属于程序不可或缺的部分。只要任何非守护线程还在运行,程序就不会终止;所有守护线程结束时程序才终止。),target表示要执行的任务。

还有一些常用的关系到线程运行状态的方法。start(),run(),sleep(),yield(),join()等。

用java继承Thread类,实现多线程。由于是竞争资源,所以可能每次输出结果不同。同样的i,有时A先有时B先,或者领先几个数,都有可能。

package multithreading;
//Thread类中run()和start()方法的区别如下:
//run()方法:在本线程内调用该Runnable对象的run()方法,可以重复多次调用;
//start()方法:启动一个线程,调用该Runnable对象的run()方法,不能多次启动一个线程;

	//Java 中实现多线程,有两个手段 1.继续Thread类  2.实现Runnable接口 (3.实现Callable接口)
	//扩展java.lang.Thread 类
	class Thread1 extends Thread{  // 继承Thread类,作为线程的实现类
		    private String name;  
		    
		    public Thread1(String name) {  
		       this.name=name;  
		    }  
		    //覆写run()方法,作为线程的操作主体(线程体),并不是启动线程
		    //包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止
		    //run()方法只是普通方法,在主线程里执行,程序执行路径只有一条
		    //run()方法必须是public访问权限,返回值类型为void.
		    public void run() {  
		        for (int i = 0; i < 5; i++) {  
		            System.out.println(name + "运行  :  " + i);  
		            try {  
		                sleep((int) Math.random() * 10);  
		            } catch (InterruptedException e) {  //捕获中断异常 (大概就是在多线程中会出现的异常)
		                e.printStackTrace();  //try异常,执行catch语句,系统自动将catch括号中实例化Exception类型的对象,e是对象的引用名称
		                //e(引用)会自动调用Exception类中指定的方法,也就出现了e.printStackTrace() ;
		                //printStackTrace() 在命令行打印异常信息在程序中出错的位置及原因
		            }  
		        }  
		         
		    }  
	 
		  //程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()调用时候被创建。随着调用MitiSay的两个对象的start方法,
		    //另外两个线程也启动了,这样,整个应用就在多线程下运行。
		    public static void main(String[] args) {  
		        Thread1 mTh1=new Thread1("A");  //实例化对象
		        Thread1 mTh2=new Thread1("B"); 
		        //start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable),
		        //什么时候运行是由操作系统决定的。
		        mTh1.start();  //调用线程主体,启动线程 真正实现多线程运行,无需等待run方法体代码执行完毕而可直接继续执行下面的代码。
		        mTh2.start();  //start 处于runnable状态,一旦得到CPU时间片,立刻执行run()方法。
		  
		    }  
		  
	} 

       

Runable接口:只有一个抽象方法Run().所有的多线程代码都在run方法里面。

package multithreading;
public class thread2 implements Runnable{

	private String name ;
	
	
//	@Override
	public thread2(String name){
		this.name = name;
	}
	//@Override //为什么这里要去掉 Override 重写 的注释 还不清楚
	public void run(){
		for(int i = 0;i < 5;i++){
			System.out.println(name + "--:" + i);
			try{
				Thread.sleep((int) Math.random()*10);
			} catch (InterruptedException e){
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args){	
		//thread2 t2 = new thread2("c");
		//Thread mTh2 = new Thread(t2);
		//mTh2.start();//这三行跟下面一行,一个意思
        new Thread(new thread2("C")).start();  
        new Thread(new thread2("D")).start();  
	}
}


		thread2 t2 = new thread2("c");  //也可行,因为是使用了Runnable接口,thread2 没有 Thread的start()方法,
		Thread mTh2 = new Thread(t2);   //所以要把t2线程放在 Thread类里面才能够用start()方法。
		mTh2.start();

线程同步

1.同步 synchronized 关键字的作用域:

    a. 某个对象实例内,synchronized aMethod(){ } //防止多个线程同时访问本对象,

     b. 某个类内,synchronized static aStaticMethod{}防止多个线程同时访问此类中的synchronized static方法。可以对类的所有对象实例起作用。

2. synchronized 关键字 不能被继承,父类的 synchronized fun1(){ }; 在子类中 变成了 fun1(){ };

3. 还可修饰函数,或者函数内的语句块,可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。

使用同步开销大,还可能造成死锁,所以尽量避免无谓的同步控制。

注意:

1、线程同步的目的是为了保护多个线程仿问一个资源时对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有且仅有一个锁,这个锁有一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他非同步方法。
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
4、对于同步,要时刻清醒在哪个对象上同步,这是关键。
5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。转载自【https://www.cnblogs.com/GarfieldEr007/p/5746362.html】

下面实现循环打印A,B,C 循环10次。使用多线程。ABCABCABCABC。。。。。。

package multithreading;
//建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C,要求线程同时运行,交替打印10次ABC。
//建立3个对象,虽然是三个普通的Object,却在run()中通过同步、等待、释放锁来  实现线程的执行顺序
public class ManyThreadPrint implements Runnable{
	public String name;
	public Object prev;
	public Object self;
	
	public ManyThreadPrint (String name ,Object prev,Object self){
		this.name = name;
		this.prev = prev;
		this.self = self;
	}
	
	public void run(){
		int count =10;
		while(count > 0 ){
			synchronized (prev){
				synchronized (self){
					System.out.print(name);
					count--;
					self.notify();
				}
				try{
					prev.wait();
				}catch(InterruptedException e){
					e.printStackTrace();
				}
			}
		}
	}
	public static void main(String[] args) throws InterruptedException{
		// TODO 自动生成的方法存根
		Object a = new Object();
		Object b = new Object();
		Object c = new Object();
		ManyThreadPrint mtpa = new ManyThreadPrint("A",c,a);
		ManyThreadPrint mtpb = new ManyThreadPrint("B",a,b);
		ManyThreadPrint mtpc = new ManyThreadPrint("C",b,c);
		new Thread(mtpa).start();
		Thread.sleep(1);
		new Thread(mtpb).start();
		Thread.sleep(1);
		new Thread(mtpc).start();
		Thread.sleep(1);
		
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值