JAVA SE(十九)—— JAVA 线程1(基本概念、创建线程、生命周期、Thread类方法、守护线程)

一、线程安全

1、基本概念
(1)线程安全
如果有多个线程同时运行同一段逻辑处理代码,如果每次运行的结果与单线程时候运行的结果是一样的,就说他是线程安全的。

(2)并行
在同一时间点同时运行,强调的是时间点,是真正的同时运行。

(3)并发
同一时间段内运行同一段逻辑,强调的是时间段,没有真正运行。

(4)同步
用于确保临界资源一次只能被一个线程使用,用来保证数据的准确性。

(5)异步
线程之间互不干扰,各自运行,缺点是会造成临界资源数据不安全

(6)临界资源
多线程共享的示例变量,或者多线程共享的公共静态变量

二、关键字:synchronized(锁)

1、格式

synchronized( 任意对象 ){

    需要枷锁的代码块; 

}

类锁与对象锁互不干扰

2、分类
(1) 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{ }括起来的代码,作用的对象是调用这个代码块的对象;

synchronized (obj) {
    System.out.println( Thread.currentThread( ).getName( ) + i );
}

(2)修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;

public synchronized void demo1( ){
    方法的执行内容;
}

其等价于:

public void demo2( ) {
    synchronized( this ) {
        方法执行内容;
    }
}

(3)修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

public static synchronized void demo4( ) {
    方法执行内容;
}

(4)修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象。

public void demo2( ) {
    synchronized( this ) {
        方法执行内容;
    }
}

其与第三种效果相同。

(5)synchronized锁Demo

public class SnycDemo {
	public Object obj = new Object( );
	//锁代码块
	public void demo0( ) {
		try {
			for( int i = 0 ; i < 100 ; i ++ ) {
				synchronized (obj) {
					System.out.println( Thread.currentThread( ).getName( ) + i );
				}
			}
		} catch (Exception e) {
			e.printStackTrace( );
		}
	}
	//对象锁
	public synchronized void demo1( ) {
		try {
			for( int i = 0 ; i < 100 ; i ++ ) {
				System.out.println( Thread.currentThread( ).getName( ) + i );
			}
		} catch (Exception e) {
			e.printStackTrace( );
		}
	}
	//对象锁
	public void demo2( ) {
		synchronized( this ) {
			try {
				for( int i = 0 ; i < 100 ; i ++ ) {
					System.out.println( Thread.currentThread( ).getName( ) + i );
				}
			} catch (Exception e) {
				e.printStackTrace( );
			}
		}
	}
	//类锁
	public void demo3( ) {
		synchronized( SnycDemo.class) {
			try {
				for( int i = 0 ; i < 100 ; i ++ ) {
					System.out.println( Thread.currentThread( ).getName( ) + i );
				}
			} catch (Exception e) {
				e.printStackTrace( );
			}
		}
	}
	//类锁
	public static synchronized void demo4( ) {
		try {
			for( int i = 0 ; i < 100 ; i ++ ) {
				System.out.println( Thread.currentThread( ).getName( ) + i );
			}
		} catch (Exception e) {
			e.printStackTrace( );
		}
	}
}

三、死锁

1、死锁产生条件
(1)互斥使用:当一个对象占有一个资源时,另外一个不能拥有;

(2)不可抢占:资源请求者不能强制从资源占有者手中抢夺资源,资源只能由占有者主动释放;

(3)请求和保持:资源请求者在请求其他资源时,保持对现有资源的占有;

(4)循环等待:存在一个等待队列,如p1占有p2的资源,p2占有p1的资源。

(5)死锁Demo

public class DeadSync {
	//死锁
	public static String left = "资源1";
	public static String right = "资源2";
	public static void main(String[] args) {
		new Thread( "用户1" ) {
			public void run( ) {
				synchronized (left) {
					try {
						System.out.println( Thread.currentThread( ).getName( ) +  "抢夺到资源1" );
						Thread.sleep(1000);
						System.out.println( Thread.currentThread( ).getName( ) + "开始抢夺到资源2" );
						synchronized (right) {
							System.out.println( Thread.currentThread( ).getName( ) + "抢夺到资源2" );
						}
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}.start( );
		new Thread( "用户2"  ) {
			public void run( ) {
				synchronized (right) {
					try {
						System.out.println( Thread.currentThread( ).getName( ) +  "抢夺到资源2" );
						Thread.sleep(1000);
						System.out.println( Thread.currentThread( ).getName( ) + "开始抢夺到资源1" );
						synchronized (left) {
							System.out.println( Thread.currentThread( ).getName( ) + "抢夺到资源1" );
						}
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}.start( );
	}
}

上面Demo2的运行结果为

用户1抢夺到资源1
用户2抢夺到资源2
用户1开始抢夺到资源2
用户2开始抢夺到资源1

用户1和用户2分别抢占资源1和资源2,一开始两个用户各自抢到一个资源,当开始抢另一个资源的时候,由于加了锁,双方都会对等待对方交出“锁的钥匙”,即占有权,并且都不会放弃现有资源的占有权,所以会出现程序没有结束,但也不能往下运行的结果,这就叫出现死锁。

我们在编写程序的时候,应尽量避免出现这种情况。

四、线程通信

1、概念
多个线程在处理同一个资源,但是处理的动作却不相同,通过一定的手段, 使各个线程能够有效地利用资源,这种手段就叫做线程间的通信(等待唤醒机制)。

2、方法(属于object类)

  • wait( )方法 等,无线等;
  • notify( )方法 唤醒:唤醒被wait( )的线程,只随机唤醒一个;
  • notifAll( )方法 唤醒所有被wait( )的线程。唤醒:让线程具备执行的资格,能够被CPU调用。

3、wait( )和sleep( )区别

  • wait( )属于Object类,sleep( )属于Thread类;
  • wait( )在睡眠时会释放锁资源,sleep( )不会释放锁资源;
  • wait( )只能在同步代码块使用,sleep( )可以在任何地方使用;
  • sleep( )必须捕获异常try…catch,而wait( )不需要;
  • sleep( )如果不捕获异常,线程自动死亡,wait( )抛出异常后线程会继续运行。

4、方法使用注意事项

  • 必须在同步代码块中才有效;
  • 使用这些方法时,必须标明所属锁;

五、线程池

1、概念

  • 线程池就是首先创建一些线程,它们的集合称为线程池。
  • 使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程, 程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务, 执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。

2、作用
(1)避免重复创建及销毁线程
(2)达到线程的重用
线程池的返回值 ExecutorService 是Java提供的用于管理线程池的类,该类的两个作用:控制线程数量和重用线程

3、常用线程池
(1)可缓存线程池: Executors.newCacheThreadPool( )
先查看池中有没有以前建立的线程,如果有,就直接使用。如果没有,就建一个新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务
线程池为无限大,当执行当前任务时上一个任务已经完成,会复用执行上一个任务的线程,而不用每次新建线程

(2)可重用固定个数的线程池: Executors.newFixedThreadPool(int n)
创建一个可重用固定个数的线程池,以共享的无界队列方式来运行这些线程。定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()

(3)定长线程池: Executors.newScheduledThreadPool(int n)
创建一个定长线程池,支持定时及周期性任务执行

(4)单线程化的线程池: Executors.newSingleThreadExecutor( )
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

六、线程示例

1、Demo1:有一个水池容量100L,编写两个线程(进水口、出水口),要求两个线程不能同时运行,充水完成立马放水,放水完成立马充水,充水速度5L/s,放水速度2L/s。
定义一个Flag类,里面有一个boolean类型的变量flag,用于判断充水或放水是否完成;

public class Flag {
	public boolean flag;
}

定义两个线程(进水口waterIn和出水口waterOut)
进水口类

public class WaterIn extends Thread{
	Flag f = new Flag( );
	public WaterIn( ) { }
	public WaterIn( Flag f ) {
		this.f = f;
	}
	//重写run()方法
	public void run( ) {
		while( true ) {
			synchronized ( f ) {
				if( f.flag ) {
					try {
						f.wait( );
					} catch (InterruptedException e) {
						e.printStackTrace( );
					}
				} else {
					f.notify( );
					for ( int i = 0 ; i <= 20 ; i++) {
						try {
							Thread.sleep(100);
							System.out.println( "充水" + 5*i + "L" );
							f.flag = true;
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					System.out.println( "充水完成" );
				}
			}
		}
	}
}

出水口类

public class WaterOut extends Thread{
	Flag f = new Flag( );
	public WaterOut( ) { }
	public WaterOut(Flag f) {
		this.f = f;
	}
	//重写run()方法
	public void run( ) {
		while( true ) {
			synchronized ( f ) {
				if( f.flag ) {
					f.notify( );
					for( int i = 50 ; i >= 0 ; i -- ) {
						try {
							Thread.sleep(100);
							System.out.println( "放水" + ( 100 - 2*i ) + "L");
							f.flag = false;
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					System.out.println( "放水完成");
				}else {
					try {
						f.wait( );
					} catch (InterruptedException e) {
						e.printStackTrace( );
					}
				}
			}
		}
	}
}

主函数类,用于调用方法,实现功能。

public class Demo1 {
	public static void main(String[] args) {
		Flag f = new Flag();
		WaterIn wi = new WaterIn(f);
		WaterOut wo = new WaterOut(f);
		wi.start();
		wo.start();
	}
}

2、Demo2:按照规律"12a34b56c…5152z"打印数字和字母的组合。
① 定义一个Flag类,里面有一个boolean类型的变量flag,用于判断充水或放水是否完成;

public class Flag {
	public boolean flag;
}

打印数字的类

public class PrintNum extends Thread {
	Flag f = new Flag( );
	public PrintNum( ) {}
	public PrintNum(Flag f) {
		this.f = f;
	}
	public void run( ) {
		synchronized ( f ) {
			for ( int i = 1 ; i <= 52 ; i++) {
				if( f.flag ) {
					try {
						f.wait( );
					} catch (InterruptedException e) {
						e.printStackTrace( );
					}
				} else {
					f.notify( );
					System.out.print( i );
					System.out.print( i + 1 );
					f.flag = true;
				}
			}
		}
	}
}

打印字母类

public class PrintChar extends Thread {
	Flag f = new Flag( );
	public PrintChar() { }
	public PrintChar(Flag f) {
		this.f = f;
	}
	public void run( ) {
		synchronized ( f ) {
			for( char i = 'a' ; i <= 'z' ; i ++ ) {
				if( f.flag ) {
					f.notify( );
					System.out.print( i );
					f.flag = false;
				}else {
					try {
						i--;
						f.wait( );
					} catch (InterruptedException e) {
						e.printStackTrace( );
					}
				}
			}
		}
	}
}

main函数类

public class Demo2{
	public static void main(String[] args) {
		Flag f = new Flag();
		PrintNum pn = new PrintNum(f);
		PrintChar pc = new PrintChar(f);
		 pn.start();
		 pc.start();
	}
}

3、Demo3:窗口售票
① 售票类

public class Tickets {
	public Object obj = new Object( );
	int total = 1;
	public void saleTickets( ) {
		while( total <= 10) {
			synchronized( obj ) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				if ( total > 10) {
					System.out.println( "票卖完了" );
					break;
				}
				System.out.println( Thread.currentThread( ).getName( ) + "卖出第" + total + "张票" );
				total++;
			}
		}
	}
}

main函数类

public class Demo3{
	//卖票
	public static void main(String[] args) {
		Tickets tickets = new Tickets( );
		new Thread( "窗口1" ) {
			public void run( ) {
				tickets.saleTickets( );
			}
		}.start( );
		 new Thread( "窗口2" ) {
			public void run( ) {
				tickets.saleTickets( );
			}
		}.start( );
	}
}

4、Demo4:抽奖箱

public class Demo4{
	//抽奖箱 做法2
	public static void main(String[] args) {
		Thread t1 = new Thread( "抽奖箱1" ) {
			public void run( ) {
				for( int i = 0 ; i < 100  ; i++ ) {
					try {
						Thread.sleep(100);
						GetMoney.getMoney( );
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		};
		Thread t2 = new Thread( "抽奖箱2" ) {
			public void run( ) {
				for( int i = 0 ; i < 100 ; i ++ ) {
					try {
						Thread.sleep(100);
						GetMoney.getMoney( );
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		};
		t1.start( );
		t2.start( );
	}
}

5、Demo5:实现三个线程,一个线程打印a, 一个打印b,一个打印c,三个线程同时执行,要求打印出6个的abc

public class Demo5{
	static int num = 1;
	public static void main(String[] args) {
		Object obj = new Object();
		for(int i = 0; i < 6; i++) {
			new Thread() {
				@Override
				public void run() {
					synchronized (obj) {
						while(num != 1) {
							try {
								obj.wait();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
						System.out.print( 'a');
						num = 2;
						obj.notifyAll();
					}
				}
			}.start();
			new Thread() {
				@Override
				public void run() {
					synchronized (obj) {
						while(num != 2) {
							try {
								obj.wait();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
						System.out.print('b');
						num = 3;
						obj.notifyAll();
					}
				}
			}.start();
			new Thread() {
				@Override
				public void run() {
					synchronized (obj) {
						while(num != 3) {
							try {
								obj.wait();
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
						System.out.println('c');
						num = 1;
						obj.notifyAll();
					}
				}
			}.start();
		}
	}
}

6、Demo6:某公司组织年会,会议入场时有两个入口,在入场时每位员工都能获取一张双色球彩票,假设公司有100个员工,利用多线程模拟年会入场过程,并分别统计每个入口入场的人数,以及每个员工拿到的彩票的号码。线程运行后打印格式如下:
编号为: 2 的员工 从后门 入场! 拿到的双色球彩票号码是: [17, 24, 29, 30, 31, 32, 07]
编号为: 1 的员工 从后门 入场! 拿到的双色球彩票号码是: [06, 11, 14, 22, 29, 32, 15]

从后门入场的员工总共: 13 位员工
从前门入场的员工总共: 87 位员工

线程类:

public class ColorBall {
	public Object obj = new Object( );
	int total = 1;
	int a = 0;
	int b = 0;
	public void party( ) {
		while( total <= 100 ) {
			int[] arr = new int[7];
			arr[6] = (int)( 16 * ( Math.random( ) ) + 1 );
			for( int i = 0 ; i < 6 ; i++ ) {
				arr[i]= (int)( 33 * ( Math.random( ) ) + 1 );
				for( int j = 0 ; j < i ; j++ ){
					if ( arr[i] == arr[j] ) {
					--i;
					continue;
					}
				}
			}
			String str = "[" + arr[0] + "," + arr[1] + "," + arr[2] + "," + arr[3] + "," + arr[4] + "," + arr[5] + "," + arr[6] + "]";
			
			//员工入场
			synchronized (obj) {
				try {
					Thread.sleep(10);
					if( total <= 100 ){
						//统计前后门进场员工人数
						if ( Thread.currentThread( ).getName( ).equals( "前门" ) ) {
							a++;
						} else if (Thread.currentThread( ).getName( ).equals( "后门" ) ) {
							b++;
						}
						System.out.println( "编号为:" + total + "的员工从" + Thread.currentThread( ).getName( ) + "入场,拿到的双色球彩票号码是:" + str );
						total++;
					} else {
							System.out.println( "从前门入场的员工总共:" + a + "个员工");
							System.out.println( "从后门入场的员工总共:" + b + "个员工");
					}
				} catch (InterruptedException e) {
					e.printStackTrace( );
				}
			}	
		}
	}
}

main函数类

public class Dem6 {
	public static void main(String[] args) {
		ColorBall cb = new ColorBall( );
		new Thread( "前门" ) {
			public void run( ) {
				cb.party( );
			}
		}.start( );
		 new Thread( "后门" ) {
			public void run( ) {
				cb.party( );
			}
		}.start( );
	}
}
发布了40 篇原创文章 · 获赞 0 · 访问量 355

猜你喜欢

转载自blog.csdn.net/baidu_27414099/article/details/104425428