java:多线程

进程:
        进程是指可执行程序并存放在计算机储存器得一个指令序列,它是一个动态执行得过程。

        进程是系统分配资源(CPU,内存,硬盘等)的最小单位。

        操作系统是多进程的。

线程:

        线程是一个进程还要小的运行单位,一个进程包含多个线程

        主线程是程序启动得时候就开启得线程。mian是主程序得入口

             主线程中可以开启其他的线程就形成了多线程

              多个线程共享该进程中的资源

               线程其实是CPU运行得最小单位。给用户得感觉是多个线程同时在运行,但实际上不是的,是多个线程交替获取CPU的时间片,交替执行的,只是线程获取CPU的的时间比较短,是以毫秒计算的,所有人类感受不到。

多线程的好处

        提升了CPU的执行效率

        用户体验度更高

        开发人员方便开发

实现线程的两种放方法:

  • 一种是将类声明为Thread的子类
  • 一种是实现Runnable接口

Thread类;

       Java是通过Java.long.Thread类来代表线程的

        按照面对对象的思想,Thread类应该是提供了实现多线程的方式。

Thread类的构造方法
构造方法说明
Thread()创建一个线程对象
Thread(String name)创建一个具有指定名称的线程对象
Thread(Runnable targer)创建一个基于Runnable接口的实现类的线程的线程对象
Thread(Runnable target,String name)创建一个基于Runnable接口实现类党,并且具有指定名称的对象。

Thread类的常用方法
放法说明
public void run()线程相关的代码写在该方法里面,一般需要子类重写
public void start()启动线程的方法
public static void sleep(long m)线程休眠m毫秒的方法
public void join()优先执行调用join()方法的线程
public static Thread currentThread()返回对象当前正在执行的线程对象的引用
public String name()返回该线程的名称

多线程的实现方案一:继承Thread类

        1.定义一个子类MyThread继承类Thread,并重写run()方法

        2.创建MyThread类的对象

        3.调用线程对象的start()方法启动线程(启动线程后又JVM虚拟机自动调用run()方法)

优缺点:

        优点:编码简单

        缺点:线程已经继承Thread,无法再继承别的类。

讨论:

1.为什么不直接调用run()方法,而是调用start()方法?

  • 直接调用run()方法会被当作普通方法执行,此时还是相当于单线程执行。
  • 只有调用了start()方法才是启动一个线程执行。

代码演示:

public class Test {
	public static void main(String[] args) {
		MyThread m = new MyThread();
		MyThread1 m1 = new MyThread1();
		m.run();
		m1.run();
	}

}

class MyThread extends Thread{
	public static int a;
	@Override
	public void run() {
		for (int i = 0; i <= 10; i++) {
			System.out.print( " MyThread: " + i + "  ");
			a++;
			if (this.a % 5==0) {
				System.out.println("\n");
			}
		}
	}
}
class MyThread1 extends Thread{
	public static int a;
	@Override
	public void run() {
		for (int i = 0; i <= 10; i++) {
			System.out.print(" MyThread1: " + i + "  ");
			a++;
			if (this.a % 5==0) {
				System.out.println("\n");
			}
		}
	}
}

执行结果:

 

 当调用start()方法时:

public class Test {
	public static void main(String[] args) {
		MyThread m = new MyThread();
		MyThread1 m1 = new MyThread1();
		m.start();
		m1.start();
		
	}

}

class MyThread extends Thread{
	public static int a;
	@Override
	public void run() {
		for (int i = 0; i <= 10; i++) {
			System.out.print( " MyThread: " + i + "  ");
			a++;
			if (this.a % 5==0) {
				System.out.println("\n");
			}
		}
	}
}
class MyThread1 extends Thread{
	public static int a;
	@Override
	public void run() {
		for (int i = 0; i <= 10; i++) {
			System.out.print(" MyThread1: " + i + "  ");
			a++;
			if (this.a % 5==0) {
				System.out.println("\n");
			}
		}
	}
}

执行结果:

 

2.把主线程任务放在子线程之前了。

  • 这样主线程一直是先跑完,相当于是一个单线程的效果了

代码演示:

public class Test {
	public static void main(String[] args) {
		
		for (int i = 0; i < 10; i++) {
			System.out.println("main : " + i);
			
		}
		MyThread m = new MyThread();
		m.start();
		
	}

}

class MyThread extends Thread{
	public static int a;
	@Override
	public void run() {
		for (int i = 0; i <= 10; i++) {
			System.out.print(Thread.currentThread().getName()+ " : " + i + "  ");
			a++;
			if (this.a == 5) {
				System.out.println("\n");
			}
		}
	}
}

执行结果:

 3.如果一个线程报异常,会影响其他线程吗?

        不会影响

Runnable接口:

  • 只有一个方法run();
  • Runnable时Java中用以实现线程的接口;
  • 任何实现线程的类都必须继承实现该接口(Thread类也实现了Runnable接口); 

多线程的实现方案二:实现Runnable接口(推荐)

        1.定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法;

        2.创建MyRunnable任务对象;

        3.把MyRunnable任务对象交给Thread处理;

        4.调用线程随想的start()方法启动线程

代码演示:

public class Test {
	public static void main(String[] args) {
		MyRunnable m = new MyRunnable();
		MyRunnable1 m1 = new MyRunnable1();
		Thread t = new Thread(m);
		Thread t1 = new Thread(m1);
		t.start();
		t1.start();
		
	}

}

class MyRunnable implements Runnable{
	public static int a;
	@Override
	public void run() {
		for (int i = 0; i <= 10; i++) {
			System.out.print( " MyRunnable: " + i + "  ");
			a++;
			if (this.a % 5==0) {
				System.out.println("\n");
			}
		}
	}
}
class MyRunnable1 implements Runnable{
	public static int a;
	@Override
	public void run() {
		for (int i = 0; i <= 10; i++) {
			System.out.print(" MyRunnable1: " + i + "  ");
			a++;
			if (this.a % 5==0) {
				System.out.println("\n");
			}
		}
	}
}

执行结果:

 优缺点:

  • 优点:线程任务只是实现接口,可以继续继承和实现接口,扩展性强。
  • 缺点:编程多一层包装,如果线程有执行结果是不可以直接返回的。

线程安全问题:
        多个线程同时操作同一个共享资源的时候可能会出现业务安全问题,称为线程安全问题。

买票模型演示:

        需求:有五个窗口可以买票,一共有100张票,卖完为止。

        如果五个窗口同时在卖票,会发生说明情况。

public class ticketSys {
	public static void main(String[] args) {
		ticket m1 = new ticket();
		//创建五个线程,让他们一起工作。
		Thread t1 = new Thread(m1, "窗口2");
		Thread t2 = new Thread(m1, "窗口3");
		Thread t3 = new Thread(m1, "窗口4");
		Thread t4 = new Thread(m1, "窗口5");
		Thread t5 = new Thread(m1, "窗口1");
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
	}
	
}

class ticket implements Runnable{
	private int piao = 100;
	int count = 100;
	@Override
	public void run() {
		while(true) {
			if(piao > 0) {
				try {
					//假如卖一张票需要0.1秒
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//输出窗口还剩多少张票。
				System.out.print(Thread.currentThread().getName() + " 还剩" + --piao+ ",  ");
				count--;
				//为了输出美观,设置为没输出十次一换行。
				if (count % 10 == 0 ) {
					System.out.println();
				}
			}else {
				//如果所剩的票小于0张就停止循环
				break;
			}
		}
	}
	
}

执行结果:

         很显然上面的执行结果是不现实的,这是因为在while循环里的if判断语句中,假设线程t1抢占到了CPU的时间片进入了if判断语句,当判断语句还没有结束的时候,线程t2抢占到了CPU时间片也进入到了if判断语句,这是就会发生以上这些问题,这也叫线程安全问题。

线程安全问题出现的原因:

  • 存在多线程并发
  • 同时访问共享资源
  • 存在修改共享资源

 线程安全问题的处理方式:

        将对共享数据操作的多条语句进行原子化封装即可        注意:封装的范围不要过大

原子化:可以理解为程序运行的最小单位

用synchronized代码进行封装

synchronize(锁对象){

        需要被封装的对象

}

锁对象可以是任何一个类的对象,但是要保证全局的唯一性。也可以使用this

建议使用共享资源作为锁的对象

对于实例方法建议使用this作为锁的对象

对于静态方法建议使用字节码(类名.class)对象作为锁对象

唯一性:只能保证只有一给锁。

this:这里指的是当前对象。当是同一个对象的时候使用。

 修改上面买票模型代码:

public class ticketSys {
	public static void main(String[] args) {
		ticket m1 = new ticket();
		Thread t1 = new Thread(m1, "窗口2");
		Thread t2 = new Thread(m1, "窗口3");
		Thread t3 = new Thread(m1, "窗口4");
		Thread t4 = new Thread(m1, "窗口5");
		Thread t5 = new Thread(m1, "窗口1");
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
	}
	
}

class ticket implements Runnable{
	private int piao = 100;
	int count = 100;
	private Object lo = new Object();
	@Override
	public void run() {
		while(true) {
			synchronized (lo) {
				if(piao > 0) {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.print(Thread.currentThread().getName() + " 还剩" + --piao+ ",  ");
					count--;
					if (count % 10 == 0 ) {
						System.out.println();
					}
				}else {
					break;
				}
			}
		}
	}
	
}

执行结果为:

注意:如果一个方法当中只有同步代码块,那么这个方法就可以定义为同步方法!此时同步方法的所就是this

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值