Java——多线程

多线程在以前自学的时期,只按照教科书上敲过几个程序,对各种函数的功能理解的并不透彻,现在学习了教学视频,对这部分内容有了全新的理解。概念:首先明确其概念线程和进程是有区别的。进程是正在进行中的程序,是一个应用程序在内存中一片内存空间。而线程是指进程中能够独立执行的控制单元,线程控制着进程的执行,一个进程可以同时运行多个不同的线程。创建线程的方式:有两种:继承 Thread 类。或者 实现Runnable 接口,然后构造线程对象,把实现的类的对象作为构造线程的参数。两者都需要重写其中的run()方法,其中定义的是希望线程运行的代码。下面是实现Runnable接口的形式:

class Demo implements Runnable

 public void run()

 { 

 for(int x=1; x<=20; x++) 

 { 

 System.out.println(Thread.currentThread().getName()+"......"+x); 

 } }} 

class ThreadDemo{ 

 public static void main(String[] args) { 

 Demo d = new Demo(); Thread t1 = new Thread(d);//把d作为参数 

构造线程对象 Thread t2 = new Thread(d); t1.start(); t2.start(); }}

这两种方法更常用的是实现接口的方法,原因是 第一种创建线程是有局限性的,因为继承不能有多个父类。 Runnable 接口的出现好处:避免了单继承的局限性。 Runnable 接口的出现,更是按照面向对象的思想,将线程要运行的任务进行单独的对象封装,降低了线程对象和线程任务的耦合性。
线程安全问题:
在多线程运行的实际情况中,在有多个线程访问出现延迟,加上线程运行的随机性,导致 当多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没用执行完,另一个线程参与进来执行。导致共享数据的错误。
举例说明:
如果用如下代码 实现 多线程卖票系统:
/*
多线程实现多个窗口卖票。
*/
class Ticket implements Runnable//extends Thread
{
	private  int tick = 100;
	public void run()
	{
		while(true)
		{
			if(tick>0)
			{
				//显示线程名及余票数
				System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
			}
		}
	}
}


class  TicketDemo
{
	public static void main(String[] args) 
	{
		//创建Runnable接口子类的实例对象
		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();
	}
}
就会出现tick这个共享数据出现错误,此时需要解决安全问题就需要用同步的方法。
同步也有两种方法实现:
第一种是 同步代码块,格式
synchronized (任意对象)
{
需要被同步的代码
}
此时对象这个参数就相当于一把锁,获得锁的线程进行代码块的执行,没有锁的线程即使有cpu执行权,也无法对代码块进行执行,执行结束后释放锁,保证对共享数据操作的安全性。
修改后代码如下
class Ticket implements Runnable
{
	private int tick=100;
	Object obj = new Object();
	public void run()
	{
		while(true)
		{
			//同步代码块
			synchronized(obj)
			{
				if(tick>0)
				{
					try
					{	
						//使用线程中的sleep方法,模拟线程出现的安全问题
						//因为sleep方法有异常声明,所以这里要对其进行处理
						Thread.sleep(10);
					}
					catch (Exception e)
					{
					}
					System.out.println(Thread.currentThread().getName()+"..tick="+tick--);
				}
			}	
		}
	}
}

第二种是同步函数,只需在需要同步的函数用synchronized修饰即可。同步代码块需要建立一个任意对象作为锁,而同步函数则默认本类对象的引用为锁,就是this。
格式如下
class Ticket implements Runnable
{
	private int tick=100;
	Object obj = new Object();
	public void run()
	{
		while(true)
		{
			show();
		}
	}
	public synchronized void show()//直接在函数上用synchronized修饰即可实现同步
	{
		if(tick>0)
	    	{
			try
			{	
				Thread.sleep(10);
			}
			catch (Exception e)
			{
			}
			System.out.println(Thread.currentThread().getName()+"..tick="+tick--);
		}
	}
}	


对于静态函数的同步需要注意,静态函数使用的锁是本类对象加载过程中生成的.class字节码文件,在synchronized参数里改为(类名.class)即可。
注意:同步函数的前提,是保证共享数据正确处理的条件,有两条。1.必须有两个或两个以上的线程 2.这些线程必须使用同一个锁。后者往往容易是出错的地方!
死锁死锁指的在同步出现嵌套的时候,两个或两个以上线程同时在等待另一个已经上锁的同步代码块。
下面是死锁的代码示例:
class Lock
{
	static Object lock1 = new Object();
	static Object lock2 = new Object();
}

class Run implements Runnable
{
	private boolean flag ;
	Run(boolean flag)
	{
		this.flag = flag;
	}
	
	public void run()
	{
		if(flag == true)
		{
			synchronized(Lock.lock1)
			{
				System.out.println("true lock1");
				synchronized(Lock.lock2)
				{
					System.out.println("true lock2");
				}
			}
		}
		else 
		{
			synchronized(Lock.lock2)
			{
				System.out.println("false lock2");
				synchronized(Lock.lock1)
				{
					System.out.println("false lock1");
				}
			}
		}

	}
}

class DeadLockTest
{
	public static void main(String[] args)
	{
		Thread t1 = new Thread(new Run(true));
		Thread t2 = new Thread(new Run(false));
		
		t1.start();
		t2.start();
	}
}

线程间的通信:
在多个线程同时对共享数据进行操作,但是操作的动作不同时,就要进行线程间通信,协调操作。
下面是典型的消费者生产者问题,对“产品”进行生产和输出,要求生产一个消费一个。
代码如下:
class Res//表示资源 就是产品
{
	private String name;
	private int count = 1;
	private boolean flag = false;
	
	public synchronized void set(String name)
	{
		while(flag){
			try{
				wait();
			}catch(InterruptedException e){
				
			}
		}
		this.name = name + "--" + count++;
		flag = true;
		this.notifyAll();//唤醒等待线程
		System.out.println(Thread.currentThread().getName() +"--producer" + this.name);
	}
	public synchronized void out()
	{
		while(!flag){
			try{
				wait();
			}catch(InterruptedException e){
				
			}
		}
		flag = false;
		this.notifyAll();
		System.out.println(Thread.currentThread().getName() +"--consumer--" + this.name);
	}
}

class Producer implements Runnable
{
	private Res r;
	
	public Producer(Res r)
	{
		this.r = r;
	}
	public void run()
	{
		for(int i = 0; i<100; i++)
		{
			r.set("--goods-----");//生产产品 set()函数设置属性
		}
	}
}

class Consumer implements Runnable
{
	private Res r;
	
	public Consumer(Res r)
	{
		this.r = r;
	}
	public void run()
	{
		for(int i = 0; i<100; i++)
		{
			r.out();//输出产品信息,表示消费产品
		}
	}
}

class ProducerConsumerTest
{
	public static void main(String[] args)
	{
		Res r = new Res();
		Producer pro = new Producer(r);
		Consumer con = new Consumer(r);
		
		Thread t1 = new Thread(pro);
		Thread t2 = new Thread(con);
		Thread t3 = new Thread(pro);
		Thread t4 = new Thread(con);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		
	}
}

此外JDK1.5中提供了多线程升级解决方案。将同步synchronized替换成显示的Lock操作。将Object中wait,notify,notifyAll,替换成了Condition对象。该Condition对象可以通过Lock锁进行获取,并支持多个相关的Condition对象。

多线程这部分知识十分重要,对多条任务之间协调的思维是一种锻炼,以上。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值