java线程的简要介绍和测试代码

线程的实现方式

实现线程的第一种方式

//实现线程的第一中方式,使用继承Thread来实现线程
class MyThread extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 50; i++) {
			System.out.println(Thread.currentThread().getName().toString()+"_"+i);
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace(); 
			}
		 
			}
			
		}
	}
}

这样在mian方法中可以直接使用:
MyThread md = new MyThread();
md.start();
方式来启动线程

实现线程的第二种方式


//实现线程的第二个方式,实现Runable接口,并将其对象赋值给Thread来实现线程
//它将线程作为一个类传递过去
class MyRunable implements Runnable{
	@Override
	public void run() {
		for (int i = 0; i < 50; i++) {
			System.out.println(Thread.currentThread().getName().toString()+"_"+i);
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

这样实现的线程, 在main方法中的调用方式是:
MyRunable mt = new MyRunable();
Thread t2 = new Thread(mt);
t2.start();

注:以上两种方法实现线程的main函数调用代码如下:

public static void main(String[] args) {
//		MyThread md = new MyThread();
//		md.start();//准备就绪,启动线程
		
		MyRunable mt = new MyRunable();
		Thread t2 = new Thread(mt);
		t2.start();
}

线程的中断方式

用 interrupt 中断标记来中断线程

//用interrupt中断标记来中断线程
class MyRunnalbe2 implements Runnable{
	@Override
	public void run() { 
		for(int i=0;i<50;i++) {
		//使用Thread.interrupted()方法测试中断状态, 此方法会清除中断标记
			if(Thread.interrupted()) {
				break;
			}
			System.out.println(Thread.currentThread().getName()+"--"+i);
			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				//标记到此会抛出异常并清除标记,需要重新标记
				Thread.currentThread().interrupt();
			}
		} 
	}
}

这样,在main函数中通过调用该线程的对象的interrupt方法:
t.interrupt();
来结束线程,这种方法需要在抛出异常的时候重新标记中断,所以不被使用

用自定义标记 flag 来中断线程

//用flag 标记来中断线程

class MyRunnalbe3 implements Runnable{
	public boolean flag = true;
	
	public   MyRunnalbe3() {
		flag=true;
	}
	
	
	@Override
	public void run() {
			int i=0;
		while(flag) { 
			System.out.println(Thread.currentThread().getName()+"---"+(i++));
			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} 
		} 
	}
}

这样,在main方法中可以通过调用该对象更改flag值来中断线程:
mt.flag=false;
这种方式较为简单,比较常

注:以上以上两种中断线程的测试代码如下:

public static void main(String[] args) {
 for(int i=0;i<50;i++) {
			 System.out.println(Thread.currentThread().getName()+"--"+i);
			 try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			 if(i==20) {
				 //t.interrupt();//作一个中断线程的标记
				 mt.flag=false;//到此处用标记直接停止
		 }
	}

join的使用

join的作用是当一个线程调用它的join方法时,程序等待该线程执行完毕再继续其他线程。当其他线程在某些执行中需要使用到该线程的执行结果时,可以在该位置使用join方法,以使其他线程执行相关过程时,能够正确得到该线程的执行结果。

	public static void main(String[] args) {

		MyRunnalbe2 mt = new MyRunnalbe2();
		 Thread t = new Thread(mt);
		 t.start();
		
		 for(int i=0;i<50;i++) {
			 System.out.println(Thread.currentThread().getName()+"--"+i);
			 try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			 if(i==20) {
				 try {
					t.join();//让线程执行完毕,等待t(调用join的线程)执行完毕,main再接着执行
				} catch (InterruptedException e) {
					e.printStackTrace();
				}  
		 }
		
		}
	}

守护线程和用户线程

main线程是一个用户 ,当进程中没有用户线程时,JVM会退出
线程通过调用setDaemon方法来设置,参数为true为设置成守护线程, 参数为false为设置成用户线程
isDaemon方法将测试该线程是否为守护线程
isAlive方法测试线程是否处于活动状态:start以前处于非活动状态,start以后处于活动状态

//该测试通过将t线程设置为守护线程,并且让main线程执行速度快与t线程,可从运行结果中分析出:
		//当main线程执行完毕(即进程中没有用户线程时),JVM会退出执行(即便守护进程没有执行完毕)。
public static void main(String[] args) {
		MyRunnalbe4 mr4 = new MyRunnalbe4();
		Thread t= new Thread(mr4);
		//setDaemon将t线程设置为守护线程  
		t.setDaemon(true); 
		t.start();  
		
		for(int i = 0 ;i<50 ;i++) {
			System.out.println(Thread.currentThread().getName()+"--"+i); 
				try {
					Thread.sleep(200);//该线程执行速度较快
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
		}
}

class MyRunnalbe4 implements Runnable{
	@Override
	public void run() {
		for(int i = 0 ;i<50 ;i++) {
			System.out.println(Thread.currentThread().getName()+"--"+i);
			try {
				Thread.sleep(500);//该线程执行速度较慢
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

线程安全

多个线程共享数据时,会发生线程不安全的情况。为解决此问题,多个线程必须使用同步。
同步:多个线程在同一时间段内只有一个线程执行指定代码,其他线程要等待此线程执行完成后才可以继续执行。

使用同步代码块实现线程同步

java中同步使用synchronized将要同步的代码放到synchronized(){} 的大括号内,并用一个任意类型的变量充当锁(obj,放到小括号中),每次有线程执行大括号内代码时会将锁关闭,执行完后再将锁打开,锁被关闭时其他线程不能执行这段代码,只有等待。
用于充当锁的变量可以是任意类型。
sleep说明中“sleep不会丢失任何监视器的所有权”的意思是,当该线程处于sleep状态(让出了时间片)时,依然不丢失锁的状态(打开或关闭)。

	public static void main(String[] args) {
	//开两个线程来卖片……额,卖车票。。。
		MyRunnalbe5 mr5 = new MyRunnalbe5();
		//两个线程都用mr5赋值
		Thread t1 = new Thread(mr5);
		Thread t2 = new Thread(mr5); 
		t1.start();
		t2.start(); 
	}
	
//卖车票的线程
class MyRunnalbe5 implements Runnable{
	private int ticket=10;//票的总数
	private Object obj = new Object();//用于充当锁的任意类型变量
	@Override
	public void run() { 
	
		for (int i = 0; i < 300; i++) {
			if(ticket>0) {
				//使用线程同步synchronized
				synchronized(obj) {
					ticket--;
					
					try {
						//sleep不会丢失任何监视器的所有权
						//即当该线程处于sleep状态(让出了时间片)时,依然不丢失锁的状态(打开或关闭状态)。
						Thread.sleep(1000);
					} catch (InterruptedException e) { 
						e.printStackTrace();
					}
					System.out.println("您购买后剩余"+ticket+"张票");
				} 
			}
		}
	} 
}

使用同步方法实现线程同步

将同步代码块放到一个用synchronized标识的方法中,在相应位置调用该方法即可。

public static void main(String[] args) {
	//开两个线程
		MyRunnalbe5 mr5 = new MyRunnalbe5();
		//两个线程都用mr5赋值
		Thread t1 = new Thread(mr5);
		Thread t2 = new Thread(mr5); 
		t1.start();
		t2.start(); 
	}
	
//卖车票的线程
class MyRunnalbe5 implements Runnable{
	private int ticket=10;//票的总数
	@Override
	public void run() { 
	
		for (int i = 0; i < 300; i++) {
				method();//直接调用
		}
	} 
//同步方法同步的是当前对象
	private synchronized void method() { 
			
		if(ticket>0) { 
			ticket--; 
			try { 
				Thread.sleep(1000);
			} catch (InterruptedException e) { 
				e.printStackTrace();
			}
			System.out.println("您购买后剩余"+ticket+"张票");
		}
	}
}

使用Lock实现线程同步

Lock是一个接口,通常使用它的实现ReentrantLock来定义锁,ReentrantLock类中有很多方便的操作锁的方法,使操作更灵活。
只需要将上述代码中的method 方法改为如下即可。

	//定义一个互斥锁
	ReentrantLock lock = new ReentrantLock();
	//使用 lock实现同步
	private synchronized void method2() { 
			lock.lock();//上锁
			//使用try{}finally{} 可以避免死锁
			try {
				if(ticket>0) { 
					ticket--; 
					try { 
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println("您购买后剩余"+ticket+"张票");
				}
			}finally {
				lock.unlock();//释放锁
			}
			
		}

使用同步能解决安全问题,但是会牺牲性能。所以同步代码块尽量简短,把不随数据变化的代码尽量写在同步代码块之外。
不要阻塞:如InputStream.read(); 如果出现阻塞,会使多个线程都在此等待,都无法继续执行。
在持有锁的时候,不要对其他对象调用同步方法。因为这样容易发生死锁。

sleep 让线程进入休眠,但是不会释放对象锁
wait 让线程进入等待状态,会释放对象锁的所有权,会等待其他线程通过notify唤醒。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值