Java笔记之多线程

/*
多线程
进程:进程就是正在进行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元
线程:线程是进程中的内容或者说是进程中的一个独立的控制单元,线程控制者进程的执行,每一个进程中至少都会有一个线程
Java虚拟机启动的时候会有一个进程java.exe该进程中至少有一个线程在负责java程序的执行,而且这个线程运行的代码
存在于main方法中
多线程存在的意义:提高了程序的运行效率
*/
class ThreadMainDemo1 extends Thread //定义类继承Thread
{
	public void run() //重写run方法
	{
		for(int x=0; x<60; x++)
			System.out.println("demo run");
	}
}
class ThreadDemo1
{
	public static void main(String[] args)
	{
		ThreadMainDemo1 d = new ThreadMainDemo1(); //创建好一个对象,就创建好一个线程
		d.start(); //虚拟机调用run方法,这里叫开始运行线程了
		for(int x=0; x<60; x++)
			System.out.println("hello world!");
	}
}
/*
发现运行结果每次都不同
因为多个线程都在获取cpu的执行权,cpu执行到谁,谁就能运行。在某一个时刻,只能有一个
程序在运行(多核除外)cpu在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象
的把多线程的运行行想象为在互相抢夺cpu的执行权这就是多线程的一个特性:
随机性,谁抢到谁执行,至于执行多长时间,cpu说了算
为什么要重写run方法呢? 因为如果不重写run方法,那么你线程就没有代码可执行
因为,start()函数调用run方法,但是如果你不重写run方法,那么run方法中就没有代码
可执行,所以必须要重写run方法
创建线程的方式一:
	继承Thread类
		1.子类覆盖父类中的run方法,将线程运行代码存放在run中
		2.建立子类对象的同时线程也被创建
		3.通过调用start方法开启线程
*/

class ThreadMainDemo2 extends Thread 
{
	public void run() 
	{
		for(int x=0; x<60; x++)
			System.out.println("demo run");
	}
}
class ThreadDemo2
{
	public static void main(String[] args)
	{
		ThreadMainDemo2 d = new ThreadMainDemo2();
		d.start(); //为什么不用d.run因为,只有start能开启线程,否则是主线程在调用run,就没意义
	}
}
//练习:创建2个线程,和主线程交替运行
class ThreadMainTest1 extends Thread
{
	private String name;
	ThreadMainTest1(String name)
	{
		this.name = name;
	}

	public void run()
	{
		for(int x=0; x<60; x++)
		{
			System.out.println(name +"ThreadMainTest run..." + x);
		}
	}
}
class ThreadTest1
{
	public static void main(String[] args)
	{
		ThreadMainTest1 t1 = new ThreadMainTest1("one");
		ThreadMainTest1 t2 = new ThreadMainTest1("two");
		t1.start();
		t2.start();
		//t1.run(); //如果是执行run打印结果必然是按顺序来的,先是one,然后two,然后才是主函数的代码
		//t2.run(); //如果是执行run打印结果必然是按顺序来的,先是one,然后two,然后才是主函数的代码
		for(int x=0; x<60; x++)
		{
			System.out.println("Main..." + x);
		}
	}
}
/*
线程的运行状态
	start()		//运行
	stop()		//销毁
	sleep(time)	//冻结
	wait()		//等待
	notify()	//唤醒
*/
/*
获取线程对象以及名称
Thread.currentThread(); //获取当前线程对象
getName();		//获取线程名称
setName();		//设置线程名称,或构造函数
*/
class ThreadMainDemo3 extends Thread 
{
	public void run() 
	{
		for(int x=0; x<60; x++)
			System.out.println((Thread.currentThread()==this)+"..."+this.getName()+" demo run");
	}
}
class ThreadDemo3
{
	public static void main(String[] args)
	{
		ThreadMainDemo3 d1 = new ThreadMainDemo3();
		ThreadMainDemo3 d2 = new ThreadMainDemo3();
		ThreadMainDemo3 d3 = new ThreadMainDemo3();
		d1.start();
		d2.start();
		d3.start();
	}
}
/*
售票程序需求:
	多个窗口同时卖票

*/
class TicketMainDemo1 extends Thread 
{
	private static int ticketnum = 100; //共享100张票
	public void run() 
	{
		while(true)
		{
			if(ticketnum>0)
			{
				System.out.println(Thread.currentThread().getName()+"sale :" + ticket--);
			}
		}

	}
}
class TicketDemo
{
	public static void main(String[] args)
	{
		TicketMainDemo1 t1 = new TicketMainDemo1(); //问题:创建一个对象就有100张票,所以需要让4个对象共享100张票,这里就利用到静态属性
		TicketMainDemo1 t2 = new TicketMainDemo1();
		TicketMainDemo1 t3 = new TicketMainDemo1();
		TicketMainDemo1 t4 = new TicketMainDemo1();

		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}
/*
创建线程的第二种方法
实现Runnable接口
	1.定义类实现Runnable接口
	2.覆盖Runnable接口中的run方法
		将线程要运行的代码存放在该run方法中
	3.通过Thread类建立对象
	4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
		为什么要将Runnable接口的子类对象传递给Thread的构造函数
	     答:因为自定义的run方法所属的对象是Runnable接口的子类对象,所以要
	        让线程去指定对象的run方法,就必须明确该run方法所属的对象
	5.调用Thread类的start方法开启线程并调用Runnable接口的子类的run方法

实现方式和继承方式的区别:
	1.实现方式好处在于:避免了单继承的局限性,在定义线程时,建议使用实现方式
	2.继承Thread:线程代码存放在Thread子类run方法中
	3.实现Runnable,线程代码存放在接口的子类的run方法中
*/
class RunnableMainDemo implements Runnable 
{
	private int ticketnum = 100; //共享100张票
	public void run() 
	{
		while(true)
		{
			if(ticketnum>0)
			{
				System.out.println(Thread.currentThread().getName()+"sale :" + ticket--);
			}
		}

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

//多线程的安全问题
class TicketMainDemo2 implements Runnable
{
	private int ticknum = 100;
	public void run()
	{
		while(true)
		{
			if(ticknum>0) //线程执行到这里的时候挂了
			{
				try{Thread.sleep(10);}catch(Exception e){} //模拟线程挂掉
				System.out.println(Thread.currentThread().getName()+"...sale : " + ticknum--);
			}
		}
	}
}
class TicketDemo2
{
	public static void main(String[] args)
	{
		TicketMainDemo2 t1 = new TicketMainDemo2();
		TicketMainDemo2 t2 = new TicketMainDemo2();
		TicketMainDemo2 t3 = new TicketMainDemo2();
		TicketMainDemo2 t4 = new TicketMainDemo2();

		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}
/*
通过分析,发现,打印出0,-1,-2等错票
多线程的运行出现了安全问题:
问题的原因:
	当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完成
	另一个线程参与进来执行,导致贡献数据的错误
解决方案:
	对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中其他线程不可以参与执行
	Java对于多线程的安全问题提供了专业的解决方式,就是同步代码块
	synchronized(对象)
	{
		需要被同步的代码
	}
多线程同步代码块
对象如同锁,持有锁的线程可以在同步中执行
没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁
同步的前提:
	1.必须要有2个或2个以上的线程
	2.必须是多个线程使用同一个锁
	3.保证同步中只能有一个线程在运行
	3.保证只同步需要的代码
好处:解决了多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源
*/
class TicketMainDemo3 implements Runnable
{
	private int ticknum = 100;
	Object obj = new Object();
	public void run()
	{
		while(true)
		{
			synchronized(obj) //同步代码
			{
				if(ticknum>0)
				{
					try{Thread.sleep(10);}catch(Exception e){}
					System.out.println(Thread.currentThread().getName()+"...sale : " + ticknum--);
				}
			}

		}
	}
}
class TicketDemo3
{
	public static void main(String[] args)
	{
		TicketMainDemo3 t1 = new TicketMainDemo3();
		TicketMainDemo3 t2 = new TicketMainDemo3();
		TicketMainDemo3 t3 = new TicketMainDemo3();
		TicketMainDemo3 t4 = new TicketMainDemo3();

		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}
/*
同步函数
需求:
银行有一个金库
有2个储户分别存300元,每次存100,存3次
*/
class BankMainDemo1
{
	private int sum; //金库
	public void add(int n) //对外提供的方法,可以给金库累加
	{
		sum = sum + n;
		try{Thread.sleep(10);}catch(Exception e){}
		System.out.println("sum=" + sum); //累加一次打印一次的值
	}
}
class Cus1 implements Runnable //为什么用Runnable,因为储户里面有存钱动作,存在动作被多个人所执行,或者说被多线程所执行
{
	private BankMainDemo1 b = new BankMainDemo1();
	public void run() 
	{
		for(int x=0; x<3; x++)
		{
			b.add(100);  //存钱动作
		}
	}
}
class BankDemo1
{
	public static void main(String[] args)
	{
		Cus1 = c new Cus1();
		Thread t1 = new Thread(c);
		Thread t2 = new Thread(c);
		t1.start();
		t2.start();
	}
}
/*
目的:
该程序是否有安全问题,如果有如何解决?
如何找到问题:
	1.明确哪些代码是多线程运行代码
			public void add(int n)
			{
				sum = sum + n;
				System.out.println("sum=" + sum);
			}
			public void run() 
			{
				for(int x=0; x<3; x++)
				{
					b.add(100); //因为这里调用add方法,所以add方法也是多线程运行代码
				}
			}
	2.明确共享数据
			private BankMainDemo1 b = new BankMainDemo1();
			private int sum; //金库
	3.明确多线程运行代码中哪些语句是操作共享数据的
			sum:中有两句
				sum = sum + n; 
				sum为0,+100赋值给sum,还没等执行到下面的语句,可能会挂在这里,而后者拿到执行权
				又来执行这一句,这时sum为200,而此时,有可能前者活了,那么前者接着执行下一句,就会出现直接打印
				200的情况,100就无法打印出来,由此分析得出这两句话出了问题

				System.out.println("sum=" + sum); //累加一次打印一次的值
*/
class BankMainDemo2
{
	private int sum;
	//Object obj = new Object();
	public synchronized void add(int n)
	{
		//synchronized(obj)  //同步有问题的代码,注意只同步该同步的代码
		//同步代码块,也是一种封装形式,唯一不同的就是具有同步的特性
		//如果让函数具备同步特写呢?
		//{
			sum = sum + n;
			try{Thread.sleep(10);}catch(Exception e){}
			System.out.println("sum=" + sum);
		//}

	}
}
class Cus2 implements Runnable
{
	private BankMainDemo2 b = new BankMainDemo2();
	public void run() 
	{
		for(int x=0; x<3; x++)
		{
			b.add(100);
		}
	}
}
class BankDemo2
{
	public static void main(String[] args)
	{
		Cus2 = c new Cus2();
		Thread t1 = new Thread(c);
		Thread t2 = new Thread(c);
		t1.start();
		t2.start();
	}
}
/*
同步函数的锁是this
函数需要被对象调用,那么函数都有一个所属对象的引用,就是this
所以同步函数使用的锁是this
*/
class TicketMainDemo4 implements Runnable
{
	private int ticknum = 100;
	//public synchronized void run() 如果这么做,是不行的,因为其他线程进步来,说白了就是没搞清楚哪些需要同步,哪些不需要
	public void run()
	{
		while(true)
		{
			show(); //show方法一定会被this调用,这里其实省略了this.
		}
	}
	public synchronized void show() //单独把需要同步的代码封装,问题来的,同步函数牛逼就牛逼在锁,那么请问,锁呢? 我们把obj注释了那么这里锁在哪里呢?
	//同步函数的锁其实就是this
	{
		if(ticknum>0)
		{
			try{Thread.sleep(10);}catch(Exception e){}
			System.out.println(Thread.currentThread().getName()+"...sale : " + ticknum--);
		}
	}
}
class TicketDemo4
{
	public static void main(String[] args)
	{
		TicketMainDemo4 t = new TicketMainDemo4();
		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 TicketMainDemo5 implements Runnable
{
	private int ticknum = 100;
	Object obj = new Object();
	boolean flag = true;
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(obj) //同步代码块,这里经实验,我们得出需要换成this才行 synchronized(this)
				{
					if(ticknum>0)
					{
						try{Thread.sleep(10);}catch(Exception e){}
						System.out.println(Thread.currentThread().getName()+"...code : " + ticknum--);
					}
				}
			}
		}
		else
		{
			while(true)
			{
				show();
			}
		}

	}
	public synchronized void show() //同步函数,锁是this
	{
		if(ticknum>0)
		{
			try{Thread.sleep(10);}catch(Exception e){}
			System.out.println(Thread.currentThread().getName()+"...show... : " + ticknum--);
		}
	}
}
class TicketDemo5
{
	public static void main(String[] args)
	{
		TicketMainDemo5 t = new TicketMainDemo5();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		t1.start();
		try{Thread.sleep(10);}catch(Exception e){}
		t.flag = false; //交换标记
		t2.start();
	}
	/*
	从运行结果来看,还是不安全,原因在哪里呢?我们要想想同步的前提
	同步的前提:
	1.必须要有2个或2个以上的线程 //这个没问题
	2.必须是多个线程使用同一个锁 //可是这里,我们用的不是同一个锁,一个是obj一个是this,所以我们可以把obj对象换成this
	3.保证同步中只能有一个线程在运行
	3.保证只同步需要的代码
	*/
}
/*
静态同步函数的锁是Class对象
加static以后就不安全了,原因是静态方法用的锁不是this
因为静态方法中也不可以定义this
静态同步方法,使用的锁是该方法所在类的自解码文件对象。 类名.class
*/
class TicketMainDemo6 implements Runnable
{
	private static int ticknum = 100; //这里要static
	boolean flag = true;
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(TicketMainDemo6.class)//类名.class
				{
					if(ticknum>0)
					{
						try{Thread.sleep(10);}catch(Exception e){}
						System.out.println(Thread.currentThread().getName()+"...code : " + ticknum--);
					}
				}
			}
		}
		else
		{
			while(true)
			{
				show();
			}
		}

	}
	public static synchronized void show() //加static以后就不安全了,原因是静态方法用的锁不是this,因为静态方法是由类调用的,类加载的时候会生成自解码文件对象,该对象是类名.class 该对象是class
	{
		if(ticknum>0)
		{
			try{Thread.sleep(10);}catch(Exception e){}
			System.out.println(Thread.currentThread().getName()+"...show... : " + ticknum--);
		}
	}
}
class TicketDemo6
{
	public static void main(String[] args)
	{
		TicketMainDemo6 t = new TicketMainDemo6();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		t1.start();
		try{Thread.sleep(10);}catch(Exception e){}
		t.flag = false; //交换标记
		t2.start();
	}
}
/*
多线程-单例设计模式:
饿汉式:
*/
class Single
{
	private Single(){}
	private static final Single s = new Single();
	public static Single getInstance()
	{
		return s;
	}
}
//懒汉式:
class Single
{
	private Single(){}
	private static Single s = null;
	public static synchronized Single getInstance() //所以要加synchronized但是一旦上了锁,效率就会变低,因为每次都会去读锁
	{
		if(s==null)
		{
		//---Thread-0
		//---Thread-1
			s = new Single(); //0醒了,new一个对象,1醒了,又new一个对象
		}
		return s;
	}
}
/*
为了解决效率问题,我们可以用双重判断来解决
解决方案1:
*/
class Single
{
	private Single(){}
	private static Single s = null;
	public static Single getInstance()
	{
		if(s==null) //这里进行双重判断,如果不为空,则不再读锁,减少了判断锁的次数
		{
			synchronized(Single.class)
			{
				if(s==null)
				{
					s = new Single();
				}
			}
		}
		return s;
	}

}
//解决方案2:
class Single
{
	private Single(){}
	private static Single s = null;
	public static  Single getInstance() //所以要加synchronized但是一旦上了锁,效率就会变低,因为每次都会去读锁
	{
		if(s==null)
		{
			synchronized(Single.class)
			{
				if(s==null)
				{
					s = new Single();
				}
			}
		}
		return s;
	}
}
/*
多线程-死锁
同步当作会产生的问题-死锁
多线线程之间互相抢锁,自己都不放锁
通常死锁的出现就是同步中嵌套同步,而锁却不同
*/
//死锁例子1:
class DeadlockMainDemo1 implements Runnable
{
	private int ticknum = 100; //这里要static
	Object obj = new Object();
	boolean flag = true;
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(obj)//obj锁
				{
					show();//this锁
				}
			}
		}
		else
		{
			while(true)
			{
				 show();
			}
		}

	}
	public synchronized void show() //this锁
	{
		synchronized(obj) //obj锁
		{
			if(ticknum>0)
			{
				try{Thread.sleep(10);}catch(Exception e){}
				System.out.println(Thread.currentThread().getName()+"...code : " + ticknum--);
			}
		}
	}
}
class  DeadlockDemo1
{
	public static void main(String[] args)
	{
		DeadlockMainDemo1 d = new DeadlockMainDemo1();
		Thread d1 = new Thread(d);
		Thread d2 = new Thread(d);
		d1.start();
		try{Thread.sleep(10);}catch(Exception e){}
		d.flag = false;
		d2.start();
	}
}
//死锁例子2:
class DeadlockMainDemo2 implements Runnable
{
	private boolean flag;
	DeadlockMainDemo2(boolean flag)
	{
		this.flag = flag;
	}
	public void run()
	{
		if(flag)
		{
			synchronized(MyLock.locka) //a锁嵌套b锁
			{
				System.out.println("if locka");
				synchronized(MyLock.lockb)
				{
					System.out.println("if lockb");
				}
			}
		}
		else
		{
			synchronized(MyLock.lockb) //b锁嵌套a锁
			{
				System.out.println("else lockb");
				synchronized(MyLock.locka)
				{
					System.out.println("else locka");
				}
			}
		}
	}
}
class MyLock
{
	static Object locka = new Object();
	static Object lockb = new Object();
}
class DeadlockDemo2
{
	public static void main(String[] args)
	{
		Thread t1 = new Thread(new DeadlockMainDemo2(true));
		Thread t2 = new Thread(new DeadlockMainDemo2(false));
		t1.start();
		t2.start();
	}
}
/*
多线程之间的通信:其实就是多个线程在操作同一个资源,但是操作的动作不同
*/
//安全问题:
class ThreadResMainDemo1
{
	String name;
	String sex;
}
class Input implements Runnable
{
	private ThreadResMainDemo1 r;
	Input(ThreadResMainDemo1 r)
	{
		this.r = r;
	}
	public void run()
	{
		int x = 0;
		while(true)
		{
			if(x==0)
			{
				r.name = "没教养的牛";
				r.sex = "男";
			}
			else
			{
				r.name = "傻馒";
				r.sex = "女";
			}
			x = (x+1)%2;
		}
	}
}
class Output implements Runnable
{
	private ThreadResMainDemo1 r;
	Output(ThreadResMainDemo1 r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			System.out.println(r.name+"...."+r.sex);
		}
	}
}
class ThreadResDemo1
{
	public static void main(String[] args)
	{
		ThreadResMainDemo1 r = new ThreadResMainDemo1();
		Input in = new Input(r);
		Output out = new Output(r);
		Thread t1 = new Thread(in);
		Thread t2 = new Thread(out);
		t1.start();
		t2.start();
	}
}
/*
产生问题的原因,输入线程还没有执行完毕,被输出线程抢走了执行权,出现了安全问题,那么怎么解决呢?
*/
class ThreadResMainDemo2
{
	String name;
	String sex;
}
class Input implements Runnable
{
	private ThreadResMainDemo2 r;
	Input(ThreadResMainDemo2 r)
	{
		this.r = r;
	}
	public void run()
	{
		int x = 0;
		while(true)
		{
			synchronized(r) //添加同步代码块,使用同一把锁
			{
				if(x==0)
				{
					r.name = "没教养的牛";
					r.sex = "男";
				}
				else
				{
					r.name = "傻馒";
					r.sex = "女";
				}
				x = (x+1)%2;
			}
		}
	}
}
class Output implements Runnable
{
	private ThreadResMainDemo2 r;
	Output(ThreadResMainDemo2 r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			synchronized(r) //这里也要同步代码块,使用同一把锁
			{
				System.out.println(r.name+"...."+r.sex);
			}
		}
	}
}
class ThreadResDemo2
{
	public static void main(String[] args)
	{
		ThreadResMainDemo2 r = new ThreadResMainDemo2();
		Input in = new Input(r);
		Output out = new Output(r);
		Thread t1 = new Thread(in);
		Thread t2 = new Thread(out);
		t1.start();
		t2.start();
	}
}
/*
线程间通信-等待唤醒机制
*/
class ThreadResMainDemo3
{
	String name;
	String sex;
	boolean flag = false;  //用于判断ThreadResMainDemo3中是否有数据,当为false时,Input才能往里写,当写入完成时,置为false,并且Input进入等待状态,让它不再抢占cpu执行权
}
class Input implements Runnable
{
	private ThreadResMainDemo3 r;
	Input(ThreadResMainDemo3 r)
	{
		this.r = r;
	}
	public void run()
	{
		int x = 0;
		while(true)
		{
			synchronized(r)
			{
				if(r.flag)  //输入之前判断ThreadResMainDemo3中是否有数据,如果有就等待
					try{r.wait();}catch(Exception e){} //等待的线程,存在线程池中
				if(x==0)
				{
					r.name = "没教养的牛";
					r.sex = "男";
				}
				else
				{
					r.name = "傻馒";
					r.sex = "女";
				}
				x = (x+1)%2;
				r.flag = true; //输入完成后将标记改为true
				r.notify(); //notify唤醒的都是线程池中的线程,通常唤醒第一个被等待的
			}
		}
	}
}
class Output implements Runnable
{
	private ThreadResMainDemo3 r;
	Output(ThreadResMainDemo3 r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			synchronized(r)
			{
				if(!r.flag) //这里要取反,因为输入把它置为true了,如果ThreadResMainDemo3没数据,就不需要取数据,所以非真就开始等待
					try{r.wait();}catch(Exception e){}
				System.out.println(r.name+"...."+r.sex);
				r.flag = false; //取完数据以后把flag置为false表示ThreadResMainDemo3没数据
				r.notify(); //唤醒Input
			}
		}
	}
}
class ThreadResDemo3
{
	public static void main(String[] args)
	{
		ThreadResMainDemo3 r = new ThreadResMainDemo3();
		Input in = new Input(r);
		Output out = new Output(r);
		Thread t1 = new Thread(in);
		Thread t2 = new Thread(out);
		t1.start();
		t2.start();
	}
	/*
	wait();
	notify();
	notifyAll();
	都使用在同步中,因为要对持有监视器(锁)的线程操作
	所以要使用在同步中,因为只有同步才具有锁
	为什么这些操作线程的方法要定义在Object类中呢?
	因为这些方法在操作同步中线程时,都必须要标识它们所操作的线程只有的锁
	只有同一个锁上的被等待线程,可以被同一个锁上的notify();唤醒,不可以
	对不同锁中的线程进行唤醒
	也就是说等待和唤醒必须是同一把锁,而锁可以是任意对象,所以可以被任意
	对象调用的方法定义在Object中
	*/
}
/*
代码优化
*/
class ThreadResMainDemo4
{
	//将属性私有化
	private String name;
	private String sex;
	private boolean flag = false;
	//对外提供设置方法
	public synchronized void set(String name,String sex)
	{
		if(flag)
			try{this.wait();}catch(Exception e){}
		this.name = name;
		this.sex = sex;
		flag = true;
		this.notify();
	}
	//对外提供打印方法
	public synchronized void outPut()
	{
		if(!flag)
			try{this.wait();}catch(Exception e){}
		System.out.println(name+"...."+sex);
		flag = false;
		this.notify();
	}
}
class Input implements Runnable
{
	private ThreadResMainDemo4 r;
	Input(ThreadResMainDemo4 r)
	{
		this.r = r;
	}
	public void run()
	{
		int x = 0;
		while(true)
		{
			if(x==0)
				r.set("没教养的牛","男");
			else
				r.set("傻馒","女");
			x = (x+1)%2;
		}
	}
}
class Output implements Runnable
{
	private ThreadResMainDemo4 r;
	Output(ThreadResMainDemo4 r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			r.outPut();
		}
	}
}
class ThreadResDemo4
{
	public static void main(String[] args)
	{
		ThreadResMainDemo4 r = new ThreadResMainDemo4();
		new Thread(new Input(r)).start(); //匿名对象形式
		new Thread(new Output(r)).start();
	}
}
/*
JDK1.5以后提供了多线程升级解决方案
将同步synchronized替换成实现Lock操作
将Object中的wait,notify,notifyAll,替换了Condition对象
该对象可以Lock锁,进行获取
*/

/*
停止线程interrupt(inter乳癌普特)
1.定义循环结束标记:
	因为线程运行代码一般都是循环,只要控制了循环即可
2.使用interrupt(中断)方法
	该方法是结束线程冻结状态,使线程回到运行状态中来
Stop方法已经过时:
	那么如何停止线程呢?
     答:只有一种,run()方法结束
原理:
一般开启多线程运行,运行代码通常是循环结构的,只要控制住循环
就可以让run方法结束,也就是线程结束
当没有指定的方式让冻结的线程恢复到运行状态时,这时候需要对冻结的线程进行强制清除
强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束
Thread类提供了该方法:interrupt();
*/
class StopThreadMainDemo1 implements Runnable
{
	private boolean flag = true;
	public synchronized void run()
	{
		while(flag)
		{
			try
			{
				wait();
			}
			catch(InterruptedException e)
			{
				System.out.println(Thread.currentThread().getName()+".....Exception");
				flag = false; //清除中断状态后,将标记位置为false
			}
			System.out.println(Thread.currentThread().getName()+"...run");
		}
	}
	public void changeFlag()
	{
		flag = false;
	}
}
class StopThreadDemo1
{
	public static void main(String[] args)
	{
		StopThreadMainDemo1 st = new StopThreadMainDemo1();
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);
		t1.start();
		t2.start();

		int num = 0;
		while(true)
		{
			if(num++ == 60)
			{
				//st.changeFlag();
				t1.interrupt(); //中断状态,清除冻结状态
				t2.interrupt();
				break;
			}
			System.out.println(Thread.currentThread().getName()+"...."+num);
		}
		System.out.println("over");
	}
}
/*
守护线程setDaemon(set地模恩)
守护线程也可以理解为后台线程
*/
class StopThreadMainDemo2 implements Runnable
{
	private boolean flag = true;
	public void run()
	{
		while(flag)
		{
			System.out.println(Thread.currentThread().getName()+".....Exception");
		}
	}
	public void changeFlag()
	{
		flag = false;
	}
}
class StopThreadDemo2
{
	public static void main(String[] args)
	{
		StopThreadMainDemo2 st = new StopThreadMainDemo2();
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);

		t1.setDaemon(true); //守护线程(后台线程),该方法必须在启动线程前调用
		t2.setDaemon(true); //守护线程(后台线程),该方法必须在启动线程前调用
		//后台线程开启后和前台线程共同抢劫cpu执行权
		//当所有的前台线程结束后,后台线程自动结束
		t1.start(); //这个就叫启动线程
		t2.start();

		int num = 0;
		while(true)
		{
			if(num++ == 60)
			{
				break;
			}
			System.out.println(Thread.currentThread().getName()+"...."+num);
		}
		System.out.println("over");
	}
}
/*
Join方法(抢夺cpu执行权)
当线程执行完毕后,等待的线程才能恢复
用处:当在进行多线程运算的时候,一个线程运算过程中
可以临时加入一个线程,让这个线程运算完,其他线程
在运行
特点:
当A线程执行到了B线程的.join()方法时,A线程就会等待
等B线程执行完,A才会执行,Join可以用来临时加入线程
执行
*/
class JoinMainDemo1 implements Runnable
{
	public void run()
	{
		for(int x=0; x<70; x++)
		{
			System.out.println(Thread.currentThread().getName()+"....Join as" + x);
		}
	}
}
class JoinDemo1
{
	public static void main(String[] args)throws Exception
	{
		JoinMainDemo1 j1 = new JoinMainDemo1
		Thread t1 = new Thread(j1);
		Thread t2 = new Thread(j1);
		t1.start();
		t1.join(); //t1要申请加入到运行中来,更确定点说就是t1要执行权,也就是抢夺cpu执行权
		t2.start();

		for(int x=0; x<80; x++)
		{
			System.out.println("main...." + x);
		}
		System.out.println("Main Over");
	}
}
/*
优先级&yield方法
线程组:一般情况下,谁开启的线程,那么该线程就属于哪个组
优先级:优先级代表着抢夺资源的频率setPriority()
yield()方法用于暂停当前正在执行的线程对象,并执行其他线程
达到减缓线程的运行,能让其他线程也有平均抢资源的机会
*/
class yeieldMainDemo1 implements Runnable
{
	public void run()
	{
		for(int x=0; x<70; x++)
		{
			System.out.println(Thread.currentThread().toString()+"....Join as" + x);
			Thread.yield(); //强制让执行权临时释放
		}
	}
}
class yeieldDemo1
{
	public static void main(String[] args)throws Exception
	{
		yeieldMainDemo1 y1 = new yeieldMainDemo1();
		Thread t1 = new Thread(y1);
		Thread t2 = new Thread(y1);
		t1.start();
		t1.setPriority(Thread.MAX_PRIORITY); //设置线程优先级为10
		t2.start();

		for(int x=0; x<80; x++)
		{
			System.out.println("main...." + x);
		}
		System.out.println("Main Over");
	}
}
/*
开发时候如何正确写线程
什么时候用多线程?
答:当某些代码需要同时被执行时,就用单独的线程进行封装
*/
class ThreadMainDemo4
{
	public static void main(String[] args)
	{
		//封装第一个线程
		new Thread()
		{
			public void run()
			{
				for(int x=0; x<100; x++)
				{
					System.out.println(Thread.currentThread().getName()+"...."+x);
				}
			}
		}.start();

		//封装第二个线程
		Runnable r = new Runnable()
		{
			public void run()
			{
				for(int x=0; x<100; x++)
				{
					System.out.println(Thread.currentThread().getName()+"...."+x);
				}
			}
		};
		new Thread(r).start();

		//主线程
		for(int x=0; x<100; x++)
		{
			System.out.println(Thread.currentThread().getName()+"...."+x);
		}
	}
}
时间: 2024-11-11 22:18:55

Java笔记之多线程的相关文章

java笔记--关于多线程如何查看JVM中运行的线程

查看JVM中的线程 --如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3890280.html "谢谢-- ThreadGroup(线程组)1.一个线程的集合,也可包含其他线程组2.线程组构成一棵树,除了初始化线程组外,每一个线程组都有一个父线程组3.允许线程访问有关自己的线程组的信息,但不能访问其父线程组或其他线程组的信息 常用方法:activeCount() 返回线程组中活动线程的估计数activeGroupCount() 返回线

java笔记--关于多线程状态的理解和应用

关于多线程的状态 --如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3890266.html  "谢谢-- 线程共有6种状态:1.新建线程---使用new来新建一个线程2.运行线程---调用start()方法,线程处于运行或可运行状态3.线程阻塞---线程需要获得内置锁,当该锁被其他线程使用时,此线程处于阻塞状态4.线程等待---当线程等待其他线程通知调度表可以运行时,此时线程处于等待状态5.线程计时等待---当线程调用含有时间参数的

Java笔记五.多线程

Java中的多线程(一) 一.理解线程 1.进程.线程.多线程 1.进程:在多任务系统中,每个独立执行的程序(或说正在进行的程序)称为进程. 2.线程:一个进程中又可以包含一个或多个线程,一个线程就是一个程序内部的一条执行线索(一部分代码). 3.多线程:如果要一程序中实现多段代码同时交替运行,就需产生多个线程,并指定每个线程上所要运行的程序代码,即为多线程. 注:在单线程中,程序代码的执行是按调用顺序依次往下执行的,不能实现两端程序代码同时交替运行的效果.当我们的程序启动运行时,会自动产生一个

JAVA笔记14__多线程共享数据(同步)/ 线程死锁 /

/** * 多线程共享数据 * 线程同步:多个线程在同一个时间段只能有一个线程执行其指定代码,其他线程要等待此线程完成之后才可以继续执行. * 多线程共享数据的安全问题,使用同步解决. * 线程同步两种方法: * 1.同步代码块 * synchronized(要同步的对象){ 要同步的操作 } * 2.同步方法 * public synchronized void method(){ 要同步的操作 } */ public class Main { public static void main(

JAVA笔记:多线程的理解及应用(一)

进程与线程 进程是程序的一次动态执行过程,它经历了从代码加载.执行.执行结束的一个完整过程,这个过程也是整个进程的生命周期. 多线程是实现并发机制的一种有效手段.进程和线程一样,都是实现并发机制的基本单位. 传统的单核CPU在同一个时间段可以有多个程序在执行,但是只能有一个程序在某一时间点运行,所有的程序都要抢占CPU资源. 多核CPU下程序会并发执行. Java中多线程的实现 Java中要实现多线程可以通过以下两种方式: 1.继承Thread类 2.实现Runnable接口 1.继承Threa

Java笔记:多线程

一.意义 使用多线程的目的是为了提高CPU资源的利用效率.在单线程应用中程序必须等待当前任务的完成才能继续执行下一项任务,CPU在等待的时间内就闲置了,多线程的使用可减少闲置时间. 二.主线程 当Java程序启动时,会立即开始运行主线程.其他所有的线程都是从主线程产生的,主线程必须是最后才结束执行的线程,因为它需要执行各种关闭动作.可以通过currentThread静态方法获取主线程的引用. class Solution { public static void main(String[] ar

JAVA笔记:多线程的理解及应用(二)

Java中的主方法其实也是一个线程,就叫做主线程. 问:既然主方法是以线程的形式出现的,那么Java中至少有多少个线程呢? 答案:至少有两个线程.每次启动Java的时候其实都会启动JVM,则其中的垃圾回收机制也算是一个线程,则一共有两个线程,主线程和GC. 判断线程是否启动可以使用.isAlive方法,返回的是布尔值. 线程的强制运行 在线程的操作中,可以使用join()方法让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等该线程运行完毕才能执行. 线程的休眠 程序中可以实现线程短暂

java笔记--使用线程池优化多线程编程

使用线程池优化多线程编程 认识线程池 在Java中,所有的对象都是需要通过new操作符来创建的,如果创建大量短生命周期的对象,将会使得整个程序的性能非常的低下.这种时候就需要用到了池的技术,比如数据库连接池,线程池等. 在java1.5之后,java自带了线程池,在util包下新增了concurrent包,这个包主要作用就是介绍java线程和线程池如何使用的. 在包java.util.concurrent下的 Executors类中定义了Executor.ExecutorService.Sche

11.2-全栈Java笔记:Java中如何实现多线程

在JAVA中使用多线程非常简单,我们先学习如何创建和使用线程,然后结合案例再深入剖析线程的特性. 通过继承Thread类实现多线程 继承Thread类实现多线程的步骤: 1. 在Java中负责线程的这个功能的是java.lang.Thread 这个类 2. 可以通过创建 Thread 的实例来创建新的线程. 3.  每个线程都是通过某个特定Thread对象所对应的方法run( )来完成其操作的,方法run( )称为线程体. 4.   通过调用Thead类的start()方法来启动一个线程. [示