黑马程序员_多线程编程


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

  一、初识多线程

        不管在哪一种编程语言中,都要用到多线程的思想。比如在Android中,UI界面和各种上传、下载任务将涉及到多  线程,如果没有采用多线程的思想,那我们在使用列入拍照上传的时候,将看不到图形化的界面。

为什么要创建线程:

        在JVM的多线程机制也体现得淋漓尽致,打开JVM,至少就有两个线程在“同时”运行:主线程和垃圾回收机制,他们在不同的线程中工作,互不干扰,就算是主线程结束了,JVM也不会结束,因为在只有只要还有线程在执行(线程是由操作系统完成的,不是JVM完成的),JVM是不会结束的。

         多线程的优缺点:①优点:解决多部分代码块(任务)同时执行的问题

                                     ②缺点:虽然说多线程是在“同时”执行任务,可是那是给我们的感觉时“同时”,然而实际上多个   线程之间的的执行是不同步的(多线程的执行机制是在时间片在任务之间不停并无序地切换)。所以说如果同时有很多个线程在同时执行,线程获得时间片的机会就会越小,从而线程执行任务的额效率也就会越低。


二、创建多线程

       单线程与多线程执行过程:


         

          大概了解到什么是多线程,那就来学习如何创建线程,创建线程有两种方法:①、继承Thread类  ②、实现Runnable接口

            ①Thread类:用于描述接口的类

        方法一:继承Thread类

                     创建一个类,并继承Thread类,并覆盖Thread类中的run()方法。实例如下:

public class Demo1 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		    Show  show=new Show();
		    show.start();
	}
 
}
class Show extends Thread{

	   Show(){	   
	   }
	   
	@Override
	public void run() {
		// TODO Auto-generated method stub
		
		//使用多线程执行的代码块
		super.run();
	}
}
方法二:实现Runnable接口

创建一个,并让这个类实现Runnable接口中run()方法,实例如下:

public class Demo2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Show2 show=new Show2();
		Thread s=new Thread(show);
		s.start();

	}

}
class Show2 implements Runnable{

		Show2(){}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		//线程要执行的代码块
		
	}
	
	
	
	
}
在这里,我们虽然实现了Runnable接口,但是实现的对象不是线程对象,所以我们必须创建一个线程Thread对象, 才能开启线程(Thread s=new   Thread(show));


方法一和方法二的区别:

                              Thread类继承太繁冗。继承Thread类后,我们只需要覆盖run()方法,但是继承后我们将拥有Thread类所有的方法。

                              实现接口避免java单继承的局限性,将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务封装成对象。只需实现自己想要的方法即可。


三、线程安全性问题  

       多个线程同时操作同一个对象,将会产生什么呢?

      线程的生命周期:


    下面是三个线程同时操作一个对象的例子:

public class Demo2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Show2 show=new Show2();
		Thread s1=new Thread(show);
		Thread s2=new Thread(show);
		Thread s3=new Thread(show);
		s1.start();
		s2.start();
		s3.start();

	}

}
class Show2 implements Runnable{
            

            int num=100;
		Show2(){}
	
	@Override
	public void run() {
		
		while(num>0){
			try {
				Thread.sleep(10);
				System.out.println(Thread.currentThread().getName()+":"+num--);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
如果这是一个买票的系统,只有100张票,三个线程充当三个售票员,在售票过程则会出现售票为0或者-1,这显然是不符合实际需求的。

这就要提及到线程的安全性问题:多个线程同时操作一个共享数据且操作此共享数据的代码有多条,则会产生线程安全性问题。

                                       解决方法:使用同步代码块或同步函数

同步代码块:使用关键字synchronized,格式如下:

                       synchronized(对象){  //操作共享数据的代码块      }

同步函数:在函数名使用关键字synchronized修饰函数,格式如下:

@Override
	public synchronized void run() {
		// TODO Auto-generated method stub
		
		//使用多线程执行的代码块
		super.run();
	}
下面是多个线程操作共享资源的实例:

public class Demo2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Show2 show=new Show2();
		Show2 show2=new Show2();
		Thread s1=new Thread(show);
		Thread s2=new Thread(show);
		Thread s3=new Thread(show);
		s1.start();
		s2.start();
		s3.start();

	}

}
class Show2 implements Runnable{
	int num=100;
		Show2(){}
		 
		
	
	@Override
	public void run() {
		synchronized(Show2.class){
		while(num>0){
			try {
				Thread.sleep(10);
				System.out.println(Thread.currentThread().getName()+":"+num--);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		}
	}
}

这样就会解决上面提到的问题,原理是:当一个线程得到执行权的时候,也会获得同步锁,执行同步代码块。当cpu切换到其他线程的时候,其他线程有执行资格和执行权,但是没有得到同步锁,所以操作不了同步代码块,只有等到执行同步代码块的线程释放同步锁后,其他线程才能操作同步代码块。这样就能避免在某一时刻有多个线程同时访问共享资源,从而解决了线程的安全性问题。

同步函数和同步代码块有着相似的结构和功能,不同的是同步代码块中同步锁是任意对象,而同步函数中同步锁只能是当前对象this。

所以在考虑线程安全时建议使用同步代码块。


四、线程死锁

在多线程执行的过程中,有时候会发生一种现象:那就是线程互相需要对方的同步锁,但是自己又不释放同步锁,导致双方无限等待对方资源,从而造成死锁现象

在多线程中,出现死锁现象时,很难发生是哪里发生了死锁,所以在使用多线程时,应该小心谨慎,避免发生死锁

下面是一个死锁的实例:

public class DealLock {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		Lock lock1=new Lock(true);
		Lock lock2=new Lock(false);
		//我们说过多线程安全发生的前提是多个线程同时操作同一个资源,
				//这里我们new了两个对象资源,但是它传递的只是两个状态,出了true就是false
				//就好像多线程中同步锁的释放和获取一样
		Thread t1=new Thread(lock1);
		Thread t2=new Thread(lock2);
		t1.start();
		t2.start();

	}

}
class Lock implements Runnable{
	private boolean Tag;
	Lock(boolean Tag){
	this.Tag=Tag;
	}
	public void run(){
		
		if(Tag){
			while(true){
			synchronized(MyLock.obj1){
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+":"+"if");
				synchronized(MyLock.obj2){
					System.out.println(Thread.currentThread().getName()+":"+"if");	
			}
			}
			}
		}
		else{
			while(true){

			synchronized(MyLock.obj2){
				
				System.out.println(Thread.currentThread().getName()+":"+"else");
				synchronized(MyLock.obj1){
					System.out.println(Thread.currentThread().getName()+":"+"else");
		
			}
			}
	}
	}	
	}
	
}
class  MyLock{
	public static final Object obj1=new Object();
	public  static final Object obj2=new Object();
	
	
}


四,关于多线程的一些小细节

①,区分正在运行的线程:在创建线程子类对象时就已经产生了线程名,所以使用Thread.currentThread().getName()就能得到当前正在执行的线程

②,改变线程名称,直接调用Thread类的构造方法super(name);

③,多线程虽然解决了同步问题,但是其执行效率低。

④,执行资格:可以被cpu处理,在处理队列中等待      执行权:正在被cpu处理


---------------------- Android+J2EE开发Java培训、期待与您交流! ----------------------


### 关于Java多线程编程的拓展学习资料 #### 一、线程安全与同步机制 在Java中,为了确保多个线程能够安全地访问共享资源而不发生数据竞争,通常会采用多种方式来实现线程间的同步控制。上述提到的`VolatileAtomicThread`类展示了通过`synchronized`关键字锁定对象实例的方式来进行同步处理[^1]。 ```java public class VolatileAtomicThread implements Runnable { private int count = 0; private final Object lockObj = new Object(); @Override public void run() { for (int i = 0; i < 100; ++i) { synchronized (lockObj){ count++; System.out.println("count =======>> " + count); } } } } ``` 除了使用`synchronized`外,还可以利用更高效的原子操作(如`AtomicInteger`),以及显式的锁机制(如`ReentrantLock`)。这些方法可以减少不必要的阻塞开销并提高程序性能。 #### 二、并发工具包中的高级特性 Java提供了丰富的并发工具库——`java.util.concurrent`,其中包含了诸如`CountDownLatch`, `CyclicBarrier`, `Semaphore`等实用组件。它们可以帮助开发者更加灵活地管理复杂的多线程场景下的协作逻辑。 例如,在某些情况下可能需要等待一组线程完成特定的任务之后再继续执行后续的操作;此时就可以考虑应用`CountDownLatch`: ```java import java.util.concurrent.CountDownLatch; class Worker extends Thread{ private CountDownLatch latch; public Worker(CountDownLatch latch){ this.latch=latch; } @Override public void run(){ try { // 执行任务... Thread.sleep((long)(Math.random()*10)); // 表示当前工作已完成 latch.countDown(); } catch (InterruptedException e){} } } // 主线程创建计数器,并启动子线程们 final int N=5; CountDownLatch cdl=new CountDownLatch(N); for(int i=0;i<N;++i) new Worker(cdl).start(); try { // 等待所有子线程结束 cdl.await(); } catch (InterruptedException ignored){} System.out.println("All threads have finished."); ``` #### 三、异步通信模式的应用实践 当涉及到不同模块之间的解耦合设计时,“发布/订阅”模型是一种非常有效的解决方案之一。这里提到了一个简单的观察者接口定义[^2],它允许主体通知其注册过的监听者有关状态变化的信息。 ```java public interface Observer { void update(String message); } ``` 基于此基础之上构建完整的事件驱动架构,则可以使系统的各个部分保持松散联系的同时又能高效互动。 #### 四、数据库连接池配置优化建议 对于频繁读写的Web应用程序而言,合理设置JDBC连接参数至关重要。下面给出了一组典型的MySQL数据库URL及其用户名密码组合[^3]: ```properties jdbc.url=jdbc:mysql://localhost:3306/day06?useSSL=false&serverTimezone=UTC jdbc.user=root jdbc.password=root ``` 需要注意的是实际项目部署过程中应当遵循最小权限原则分配账户角色,并且尽可能启用加密传输选项以保障安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值