java多线程

Posted 未名胡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java多线程相关的知识,希望对你有一定的参考价值。

刚开始学多线程也是有点点蒙的感觉,就是学了就忘,因为本人还没毕业,大学的时候都是自学的,建议刚开始先理解再看视频或资料

一些定义:                                                                                                                                                                                                                   

1.JVM中的多线程解析

   JVM启动时就启动了多个线程,至少有两个线程可以分析的出来。为主线程垃圾回收的线程(gc)

多线程创建的方式一继承Tread类                                                                                                                                                 

创建新执行线程有两种方法。一种方法是将类声明为Thread的子类。

该子类重写Thread类的run方法。接下来可以分配并启动该子类的实例。

步骤:

1.定义一个类继承Thread类。

2.覆盖Thread类中的run方法。

3.直接创建Thread的子类对象创建线程。

4.调用start方法开启线程并调用线程的run方法执行。

实例一:

class Demo  extends Thread{
	private String name;
	Demo(String name){
		this.name=name;
	 }
	public void run(){
	   for(int x=0;x<10;x++){
			for(int y=-999999;y<999999999;y++){}
			System.out.println(name+"......x="+x);
	    }
	}
	public void show(){
	}
}
class ThreadDemo2{
	public static void main(String[]args){
		/*
		创建线程的目的是为了开启一条执行路径,去运行指定的代码和其他代码实现同时运行。
		而运行的指定代码就是这个执行路径的任务。
		jvm创建的主线程的任务都定义在了主函数中。
		而自定义的线程它的任务在哪儿呢?
		Thread类用于描述线程,线程是需要任务的。所以Tread类也	对任务的描述。
		这个任务就是通过Thread类中觉得方法体现。也就是说,run方法就是封装自定义线程运行任务的函数。
		run方法中定义就是线程要运行的任务代码。
		开启线性是为了运行指定代码,所以只有继承Thread类,并复写run方法。
		将运行的代码定义在run方法即可。
		*/
		Demo d1=new Demo("旺财");
		Demo d2=new Demo("xiaoqiang");
		//d1.run();
		d1.start();//开启线程,调用run方法。
		System.out.println("haha");
		//d2.run();
		d2.start();//开启线程
		
}
技术分享

问:

调用run和调用start有什么区别?

调用run方法是跟java语法中调用方法没什么区别,调用start方法则是随机执行run方法(大概的理解)

创建线程的第二种方式-实现Runnable接口                                                                                                                            

创建线程的第二种方式:实现Runnable接口。

       1.定义类实现Runnable接口。

       2.覆盖接口中的run方法,将线程的任务代码封装到run方法中。

       3.通过Thread类创建线程对象,并将Runnable接口的子类对象作为构造函数的参数进行传递。

       为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。

       所以要在线程对象创建时就必须明确要运行的任务。

       4.调用线程对象的start方法开启线程。

实例二:

class Demo  extends Thread{
    public void run(){
	for(int x=0;x<10;x++){
	     System.out.println(Thread.currentThread().getName()+"....."+x);
	 }
   }
}
class ThreadDemo{
    public static void main(String[]args){
	Demo d1=new Demo();
	Demo d2=new Demo();
	d1.start();
	d2.start();
    }
}

实例三:

class Demo implements Runnable ( extends Fu){	//准备扩展Demo类的功能,让其的内容可以作为线程的任务执行。
	//通过接口的形式完成。
	public void run(){
	    show();
	}
	public void show(){
	    for(int x=0;x<10;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);
	Thread t2=new Thread(d);
	t1.start();
	t2.start(): 
//	Demo d1=new Demo();
//	Demo d2=new Demo();
//	d1.start();
//	d2.start();
    }
}
实例四:

第二种方式的细节

class Thread{
	private Runnnable r;
	Tread(){ }
	Tread(Runnable r){
	   this.r=r;
	}
	public void run(){
	   if(r!=null)
	    r.run();
	}
	public void start(){
	    run();
	}
}
class ThreadImpl implements Runnable{
      public void run(){
	   System.out.println("runnable run");
	}
}
ThreadImpl i=new ThreadImpl();
Thread t=new Thread(i);
t.start();

class SubThread extends Thread{
    public void run(){
	  System.out.println("haha");
    }
}
//SubTread s=new SubThread();
//s.start();

第二种方式的好处

实现Runable接口好处。

1.将线程的任务从线程的子类中分离出来,进行了单独的封装。

   按照面向对象的思想将任务的封装成对象。

2.避免了java单继承的局限性。

所以,创建线程的第二个方式较为常用。


实例五:买票示例
class Ticket implement Runnable//extends Thread
{
	private( static )int num=100;
	public void run()
	{
		while(true)
		{
			if(num>0)
			{
				System.out.println(Tread.currentThread().getName()+"--------sale-----------"+num--);
			}	
		}
	}
}
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();
		
		
		Ticket t1=new Ticket();
		Ticket t2=new Ticket();
		Ticket t3=new Ticket();
		Ticket t4=new Ticket();
		
		t1.start();
		t2.start();
		t3.start();
		t4.start(); 
	}
}
线程安全问题的现象                                                                                                                                                                      

public void run()
	{
		while(true)
		{
			if(num>0)
			{
				try
				{Thread.sleep(10);}
			
			catch(InterruptedException e){}
				System.out.println(Tread.currentThread().getName()+"--------sale-----------"+num--);
				}
			}	
		}
	}
技术分享



线程安全问题产生的原因

1.多个线程在操作共享的数据。

2.操作共享数据线程代码有多条。

当一个线程在执行操作共享数据的多条代码过程中,

其他线程参与的运算。就会导致线程安全性问题的产生。

同步代码块                                                                                                                                                                                  

解决思路:

就是将多条操作共享数据的线程封装起来,当有线程执行这些代码的时候,

其他线程不可以参与运算的。必须要当前线程把这些代码都执行完毕后,

其他线程才可以参与运算。

 

在java中,用同步代码块就可以解决这个问题。

同步代码块的格式:

synchronized(对象)

{

       需要被同步的代码;

}

class Ticket implement Runnable//extends Thread
{
	private int num=100;
	Object obj=new Object();
	public void run()
	{
		
		while(true)
		{
			synchronized(obj)
			{
				if(num>0)
				{
					try(Thread.sleep(10);)
				}
				catch(InterruptedException e){}
				System.out.println(Tread.currentThread().getName()+"--------sale-----------"+num--);
			}	

		}
	}
}

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();

	}
}

同步的好处和弊端

同步好处:解决了线程的安全问题。

同步弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。

同步的前提:

同步的前提:同步中必须有多个线程并使用同一个锁。

同步函数

/*
需求:储户,两个,每个都到银行存钱每次存100,共存三次。
*/
class Bank
{
	private int sum;
	public synchronized void add(int num)//同步函数
	{
		sum=sum+num;
		
		System.out.println("sum="+sum);

	}
}
class Cus implements Runnable
{	private Bank b=new Bank();
	public void run()
	{
		for(int x=0;x<3;x++)
		{
			b.add(100);
		}
	}
}
class BankDemo
{
	public static void main(String [ ] args)
	{
		Cus c=new Cus();
		Thread t1=new Thread(c);
		Thread t2=new Thread(c);
		t1.start();
		t2.start();

	}
}

.验证同步函数的锁

同步函数的锁是this。

同步函数和同步代码块的区别:

       同步函数的锁是固定的this,

       同步代码块的锁是任意的对象。

       建议使用同步代码块。

验证静态同步函数的锁

        静态的同步函数使用的锁是   该函数所属字节码文件对象  可以用getClass方法获取。

        也可以用当前   类名.class  表示。

class Ticket implement Runnable
	private static int num=400;
//	Object obj=new Object();
	boolean flag=true;
	public void run()
	{
//		System.out.println("this:"+this);
		if(flag)
		while(true)
		{
			 synchronized(Ticket.class)//(this);
			{
				if(num>0)
				{
					try{Thread.sleep(10);}catch(InterruptedException e){}
					System.out.println(Tread.currentThread().getName()+"--------obj-----------"+num--);
				}

			}
		}
		else
		while(true)
			show();
	}
	public static synchronized void show()
	{
		if(num>0)
		{
			try{Thread.sleep(10);}catch(InterruptedException e){}
			System.out.println(Tread.currentThread().getName()+"--------funtion-----------"+num--);
		}
	}	
}
class SynFuntionLockDemo
{
	public static void main(String[ ] args)
	{
		Ticket t=new Ticket();
		Class clazz=t.getClass();
		Class clazz=Ticket.class;	//获取字节码文件对象	
//		System.out.println("t:"+t);
		Thread t1=new Thread(t);
		Thread t2=new Thread(t);
		t1.start();
		try{Thread.sleep(10);}catch(InterruptedException e){}
		t.flag=false;
		t2.start();
	}
}

单例模式涉及的多线程问题                                                                                                                                                          

/*
多线程下的单例

*/
//饿汉式
class Single
{
	private static final Single s=new Single();
	private Single(){}
	public static Single getInstance()
	{
		return s;	
	}

}

//懒汉式(面试)
class Single
{
	private static Single s=null;
	private Single(){}
	public static  Single getInstance()
	{
		if(s==null)//多加一次判断是为了解决效率问题
		{
		 synchronized (Single.class)//加线程锁是为了解决安全问题
		{
			if(s==null)
			
			s=new Single();
		}
		}
		return s;	
		
	}

}
class SingleDemo
{
	public static void main(String[]args)
	{
	
	}
}

死锁示例                                                                                                                                                                                    

/*
死锁:常见情景之一:同步嵌套。
*/
class Ticket implement Runnable
	private int num=100;
	Object obj=new Object();
	boolean flag=true;
	public void run()
	{

		if(flag)
		while(true)
		{
			 synchronized(obj);
			{
				if(num>0)
				{
					try{Thread.sleep(10);}catch(InterruptedException e){}
					System.out.println(Tread.currentThread().getName()+"--------obj-----------"+num--);
				}

			}
		}
		else
		while(true)
			show();
	}
	public synchronized void show()
	{
		 synchronized(obj)
		if(num>0)
		{
			try{Thread.sleep(10);}catch(InterruptedException e){}
			System.out.println(Tread.currentThread().getName()+"--------funtion-----------"+num--);
		}
	}	
}
class DeadLockDemo
{
	public static void main(String[ ] args)
	{
		Ticket t=new Ticket();

		Thread t1=new Thread(t);
		Thread t2=new Thread(t);
		t1.start();
		try{Thread.sleep(10);}catch(InterruptedException e){}
		t.flag=false;
		t2.start();
	}
}

****************************************************

class Test implements Runnable
{
	private boolean flag;
	Test(boolean flag)
	{
		this.flag=flag;
	}
	public void run()
	{
		if(flag)
		{
			synchronized(MyLock.locka)
			{
				synchronized(MyLock.lockb)
				{
				}	
			}	
		}
		else
		{
			synchronized(MyLock.lockb)
			{
				synchronized(MyLock.locka)
				{
				}
			}
		}
	}
}
class Mylock
{
	public static final Object locka=newObject();
	public static final Object lockb=newObject();

}
class DeadLockTest
{
	public static void main(String[]args)
	{
		Test a=new Test(true);
		Test a=new Test(false);
		Thread t1=new Thread(a);
		Thread t2=new Thread(b);
		t1.start();
		t2.start();
	}
}
线程间通信-等待唤醒机制                                                                                                                                                      

思考1:wait(),notify(),notifyAll(),用来操作线程为什么定义在Object类中?

       1,  这些方法存在于同步中。

       2,使用这些方法时必须要标识所属的同步的锁。

       3,锁可以是任意对象,所以任意对象调用的方法一定定义在Object类中。

       因为这些方法是监视器的方法,监视器其实就是锁。

思考2:wait(),sleep()有什么区别?

wait():释放cpu执行权,释放锁。

sleep():释放cpu执行权,未释放锁。

       1.wait();让线程处于冻结状态,被wait的线程会被存储到线程池中。

       2.notify();唤醒线程池中的一个线程(任意)。

       3.notifyAll();  唤醒线程池中的所有线程。

这些方法都必须定义在同步中。

因为这些方法是用于操作线程状态的方法。

必须要明确到底操作的是哪个锁上的线程。

class Resource
{
	String name;
	String sex;
	boolean flag=false;
}
//输入
class Input implements Runnable
{
	Resourec r;
	Input(Resourec r)
	{
		this.r=r ;
	}
	public void run()
	{
		int x=0;
		while(true)
		{
			synchronized(r)
			{
			if(r.flag)
			   try{r.wait();}catch(InterruptedException e){}
			if(x==0)
			{
				r.name="mike";
				r.sex="nan";
			}
			else
			{
				r.name="丽丽";
				r.sex="女女女女女";		
			}
			r.flag=true;
			r.notify();
			}
			x=x++%2;
		}
	}
}
//输出
class Output implements Runnable
{
	Resourec r;
	Output(Resourec r)
	{
		this.r=r;
	}
	public void run()
	{
		
		while(true)
		{
		  synchronized(r)
		  {
			if(!r.flag)
			  try{r.wait();}catch(InterruptedException e){}
			System.out.println(r.name+"...."+r.sex);
			r.flag=false;
			r.notify();
		  }
	     }
	}
}
class ResoureDemo2
{
	public static void main(String[]args)
	{
		//创建资源
		Resourec r=new Resurec();
		//创建任务
		Input in=new Input();
		Output out=new Output();
		//创建线程,执行路径。
		Thread t1=new Thread(in);
		Thread t2=new Thread(out);
		//开启线程
		t1.start();
		t2.start();
	}
}

线程间通信-等待唤醒机制-代码优化

class Resource
{
	 private String name;
	 private String sex;
	 private boolean flag=false;
	public synchronized void set(String name,String sex)
	{
		if(flag)
		    try{this.wait();}catch(InterruptedException e){}
		this.name=name;
		this.sex=sex;
		flag=true;
		this.notify();
	}
	public synchronized void out()
	{
		if(!flag)
		 try{this.wait();}catch(InterruptedException e){}
		System.out.println(name+"...."+sex);
		flag=false;
		notify();
	}
}
//输入
class Input implements Runnable
{
	Resourec r;
	Input(Resourec r)
	{
		this.r=r;
	}
	public void run()
	{
		int x=0;
		while(true)
		{
			if(x==0)
			{
				r.set("mike","nan");
			}
			else
			{
			          r.set("丽丽","女女女女女");		
			}
			x=x++%2;
		}
	}
}
//输出
class Output implements Runnable
{
	Resourec r;
	Output(Resourec r)
	{
		this.r=r;
	}
	public void run()
	{
		
		while(true)
		{
			r.out();
           	 }
	}
}
class ResoureDemo3
{
	public static void main(String[]args)
	{
		//创建资源
		Resourec r=new Resurec();
		//创建任务
		Input in=new Input();
		Output out=new Output();
		//创建线程,执行路径。
		Thread t1=new Thread(in);
		Thread t2=new Thread(out);
		//开启线程
		t1.start();
		t2.start();
	}
}

线程间通信-多生产者多消费者问题

                            为什么 多生产者,对消费者会出现问题,而单生产和单消费没出现问题呢?

线程间通信-多生产者多消费者问题解决

                            if判断标识只有一次,会导致不该运行的线程运行了。出现了数据错误的情况

                            while判断标识,解决了线程获取执行权后,是否要运行!

                            notify只能唤醒一个线程。如果本方唤醒本方  没意义。而且ehile判断标识+notify会导致死锁。

                            notifyAll解决了本方线程一定会唤醒对方线程的问题。

class Resoure
{
	private String name;
	private int count=1;
	private boolean flag=false;
	public synchronized void set(String name)
	{
		while(flag)
			    try{this.wait();}catch(InterruptedException e){}
		this.name=name+count;
		count++;
		System.out.println(Thread.currentThread().getNmae()+"......生产者......"+this.name);
		flg=true;
		notifyAll();
	}
	public synchronized void out()
	{
		while(!flag)
			    try{this.wait();}catch(InterruptedException e){}

		System.out.println(Thread.currentThread().getNmae()+"......消费者......"+this.name);
		flg=false;
		notifyAll();
	}

}
class Producer implements Runnable
{
	private Resource r;
	Producer(Resource r)
	{
		this.r=r;
	}
	public void run()
	{
		while(true)
		{
			r.set("烤鸭");
		}
		
	}
}
class Consumer implements Runnable
{
	private Resource r;
	 Consume(Resource r)
	{
		this.r=r;
	}
	public void run()
	{
		while(true)
		{
			r.set("烤鸭");
		}
	}
}


class ProducerConsumerDemo
{
	public static void main(Sting[]args)
	{
		Resource r=new Resource();
		Producer pro=new Producer(r);
		Consumer con=new Consumer(r);
		Thread t0=new Thread(pro);
		Thread t1=new Thread(pro);
		Thread t2=new Thread(con);
		Thread t3=new Thread(con);
		t0.start();
		t1.start();
		t2.start();
		t3.start();
	}
}

线程间通信-多生产者多消费者问题-JDK1.5新特性-Lock

JDK1.5以后将同步和锁封装成了对象。

并将操作锁的隐式方式定义到了该对象中,

将隐式动作变成了显示动作。

Lock lock=new ReentranLock();

public synchronized void set(String name)
{
	lock.lock();
	try{
		while(flag)
			    try{this.wait();}catch(InterruptedException e){}
		this.name=name+count;
		count++;
		System.out.println(Thread.currentThread().getNmae()+"......生产者......"+this.name);
		flg=true;
		notifyAll();
	}
	finally
	{
		lock.unlock();
	}
}

线程间通信-多生产者多消费者问题-JDK1.5新特性-Condition

//创建一个锁对象、
Lock lock=new ReentranLock();
//通过已有的锁获取该锁上的监视器对象。
Condition con=lock.new.Condition();

public synchronized void set(String name)
{
	lock.lock();
	try{
		while(flag)
			    //try{this.wait();}catch(InterruptedException e){}
			     try{con.await();}catch(InterruptedException e){}
		this.name=name+count;
		count++;
		System.out.println(Thread.currentThread().getNmae()+"......生产者......"+this.name);
		flg=true;
		//notifyAll();
		con.signalAll();
	}
	finally
	{
		lock.unlock();
	}
}

线程间通信-多生产者多消费者问题-JDK1.5解决办法

Lock lock=new ReentranLock();
//通过已有的锁获取该锁上的监视器对象。
//Condition con=lock.new.Condition();
//通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。
Condition producer_con=lock.newCondition();
Condition consumer_con=lock.newCondition();


public synchronized void set(String name)
{
	lock.lock();
	try{
		while(flag)
			    //try{this.wait();}catch(InterruptedException e){}
			     try{producer_con.await();}catch(InterruptedException e){}
		this.name=name+count;
		count++;
		System.out.println(Thread.currentThread().getNmae()+"......生产者......"+this.name);
		flg=true;
		//notifyAll();
		//con.signalAll();
		consumer_con.signal();
	}
	finally
	{
		lock.unlock();
	}
}


















以上是关于java多线程的主要内容,如果未能解决你的问题,请参考以下文章

java 并发多线程 : 主线程等待子线程结束的三种方式:join / CountDownLatch / CyclicBarrier

[Java][Android] 多线程同步-主线程等待全部子线程完毕案例

JAVA多线程线程安全问题产生的原因

Java多线程协作CountDownLatch,主线程等待子线程结束

Java多线程使用场景

Java多线程使用场景