黑马程序员——多线程(一)

---------------------- android培训java培训、期待与您交流! ----------------------

多线程(一)

进程:正在执行的程序。每一个进程都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。

线程:进程中的一个控制单元。线程控制着进程的执行。

一个进程至少有一个线程。

Java VM 启动的时候会有一个进程java.exe

而且这个线程运行的代码存在于main方法中,该线程称之为主线程。

线程创建方式:

方法一:

 定义Thread类的子类并重写Thread类的run方法;然后创建该线程(生成该子类的对象)。

调用线程的start方法,该方法有两个作用,启动线程,并调用run方法。

class ThreadDemo
{
	public static void main(String[] args) 
	{
		MyThread mt = new MyThread();//创建一个线程
		mt.start();//启动该线程并运行run方法 
	mt.run();// 不能直接调用run方法,否则运行多次结果是一样的,相当于普通方法的调用
		for(int j=0;j<20;j++) {
			System.out.println("main :" + j);
		}
		
	}
}

class MyThread extends Thread 
{
	public void run() {
		for(int i=0;i<20;i++) {
			System.out.println("run:"+ i);
		}
	}
}



Thread 类用于描述线程,该类就定义了一个功能,用于存放线程要运行的代码,代码就存放在run方法中。Start方法用于启动线程。

线程都有自己的名称 main方法为主线程 名称是 main 。自定义的线程名称为:Thread-编号 形式 编号从0开始。

也可以通过构造函数创建线程名称,或者setName方法

Thread.currentThread().getName() 可以获取当前线程的名称

class ThreadDemo
{
	public static void main(String[] args) 
	{
		MyThread mt1 = new MyThread("thread1");//创建一个线程
		MyThread mt2 = new MyThread("thread2");
		mt1.start();//启动该线程
		mt2.start();

		for(int j=0;j<50;j++) {
			System.out.println(Thread.currentThread().getName()+" ..main :" + j);
		}
		
	}
}

class MyThread extends Thread 
{
	//利用构造函数来设置线程的名称,也可以用setName方法
	public MyThread(String name) {
		super(name);
	}

	public void run() {
		for(int i=0;i<50;i++) {
			System.out.println(Thread.currentThread().getName()+" run:"+ i);
		}
	}
}


方法二:

1、自定义类实现Runnable接口

2、重写Runnable接口中的run方法

3、建立Thread类的线程对象

4、将Runnable接口的子类对象作为参数传递给Thread类的构造方法

5、通过调用Thread类的start方法启动线程并调用Runnable接口中的run方法代码

代码如下:

class  TicketDemo
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();
		
		Thread t1 = new Thread(t);//创建一个线程
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

class Ticket implements Runnable 
{
	private int ticket = 100;
	
	public void run() {
		while(true) {
			if(ticket>0) {
	try{
						Thread.sleep(5);//线程睡眠
					} catch(Exception e) {
						e.printStackTrace();
					}
	
				System.out.println(Thread.currentThread().getName()+ 
					" sale:"+ ticket--);
			}
		}
	}
}


实现Runnabel接口和继承Thread类的区别: 

1、实现的好处是避免了单继承的局限性。

2、线程代码存放的位置不一样:继承存放在子类的run方法中,实现存放在接口子类的run方法中。

通常使用实现Runnable接口。

多线程运行的问题:

当多条语句在操作同一个县城共享数据时,一个线程对多条语句至执行力一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。

解决方法:

对于多条操作共享数据的语句,只能让一个线程都执行完,其他线程不可以参与执行。

Java中可以使用同步代码快

格式如下:

synchronized(对象) {

需要被同步的代码.........

}

此处传递的对象如同锁,持有锁的线程可以在同步中执行。没有持有锁的线程即使有cpu的执行权,也进不去。

同步的前提:

1、必须要有两个以上的线程才同步

2、多个线程使用同一个锁

同步解决了安全问题但是也消耗了资源

对上述共享数据代码同步得到:

/*
简单的多个窗口同时售票
*/
class  TicketDemo
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();
		
		Thread t1 = new Thread(t);//创建一个线程
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

class Ticket implements Runnable 
{
	private int ticket = 200;
	Object obj = new Object();

	public void run() {
		while(true) {
			synchronized(obj) {//该对象可以是任意对象
				if(ticket>0) {
					try{
						Thread.sleep(5);
					} catch(Exception e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+ 
					" sale:"+ ticket--);
				}
			}
			
		}
	}
}


如何找出程序中多线程存在的安全问题:

1、明确哪些代码是多线程运行的代码.run方法中的代码)

2、明确共享数据(tikcet

3、明确多线程运行代码中哪些语句是操作共享数据的(对ticket的操作)

也可以对函数进行同步,用synchronized修饰函数即可。

同步函数用的锁是this

如果同步函数被Static修饰,用的锁是该方法所在类的字节码文件对象,类名.class

 

 ---------------------- android培训java培训、期待与您交流! ----------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值