Java基础------多线程

进程与线程

        进程:是操作系统中正在运行中的程序,且一个进程中至少要有一个线程

        线程:是进程中的正在运行的子程序流

        线程消耗的资源比进程小。

多线程就是一个进程中存在多个子线程,开启多个线程是为了能够同时执行多个部分的代码,但其实是系统在快速的切换运行的线程,切换也是随机的,但是切换线程是需要消耗资源的,所以如果开启大量线程会导致效率降低,多线程的好处就是可以让几个部分的代码同时运行。


在Java中通过Runtime来调用进程。

            Runtime.getRuntime.exec(程序名称);

            其返回值类型就是Process类型

       在一个进程中,可以分成主线程和子线程,只有在运行中的线程,才有机会执行代码,主线程的结束不会影响到其他正在运行的子线程,主线程停止了也就是main()方法运行结束,只有进程的所有线程都执行完毕,进程才会关闭,只要有一个线程正在运行,进程就不会退出,


线程的生命周期:

       线程的生命周期可分为:创建---运行----等待-----终止

       除了4种生命周期之外,线程还有7中状态分别是:创建状态-----可运行状态-----运行状态------阻塞状态-----等待状态------锁定状态-------终止状态

       创建状态:及在代码中定义了一个子线程对象,但是并没有执行。

       可运行状态:及调用了线程对象的start()方法,等待CPU调配资源。

       运行状态:及获得了CPU的执行权,正在运行。

       阻塞状态:及正在运行的线程调用sleep()方法,或者遇到了join()方法,进入了阻塞状态

       等待状态:及正在运行的线程调用了wait()方法,使线程进入了等待队列中

       锁定状态:及线程访问了带有锁的对象,并且改锁已经被其他线程持有,则线程进入锁定状态

       终止状态:当线程中所以代码都被执行完毕时,或者人为控制条件使线程结束,线程被销毁,及线程终止

创建线程的两种方式:

在Java中创建线程有两种方式,及继承Thread类或者实现Runnable接口

方法一:继承Thread类

     第一步:定义一个类继承Thread

     第二步:重写Thread类中的run()方法

     第三步:创建Thread子类的对象

     第四步:调用线程对象的start()方法,开启线程(只有调用start()方法,才算是开启了子线程,如果直接调用run()方法,只是执行了方法,并不是开启线程)

示例代码:

public class Test {
	public static void main(String[] args) {
		//创建线程对象
		ThreadTest thread=new ThreadTest();
		//调用start()方法启动线程
		thread.start();
	}
}

//定义一个类,并且继承Thread类
class ThreadTest extends Thread{
	//重写Thread类中的run()方法
	public void run(){
		System.out.println("我是子线程");
	}
}

方法二:实现Runnable接口

     第一步:定义一个类实现Runnable接口

     第二步:实现Runnable接口中的run()方法

     第三步:创建实现了Runnable接口的线程对象

     第四步:因为Runnable接口本身并没有start()方法,所以得借助Thread对象来开启线程,创建一个Thread对象,将实现Runnable接口的线程对像传入到 Thread对象中

     第五步:通过Thread对象的start();方法开启线程

示例代码:


public class Test {
	public static void main(String[] args) {
		//创建线程对象
		ThreadTest thread=new ThreadTest();
		//创建Thread对象,将thread以参数的形式,传入t中
		Thread t=new Thread(thread);
		//借助Thread对象的start();方法开启线程
		t.start();
	}
}

//定义一个类,并且实现Runnable接口
class ThreadTest implements Runnable{
	//实现Runnable接口中的run()方法
	public void run(){
		System.out.println("我是子线程");
	}
}
       由上面的例子可见,由于使用Thread方式来创建线程比较局限,使得该类无法再继承其他类,而Runnable方式来创建线程可以避免单继承问题,也可以共享数据,一个Runnable对象可以被多个Thread对象调用,也就是可以在多线程共享数据。示例代码:

public class Test {
	public static void main(String[] args) {
		// 创建线程对象
		ThreadTest thread = new ThreadTest();
		// 一个Runnable对象可以被多个Thread对象调用
		Thread t1 = new Thread(thread);
		Thread t2 = new Thread(thread);
		Thread t3 = new Thread(thread);
		// 借助Thread对象的start();方法开启线程
		t1.start();
		t2.start();
		t3.start();
	}
}

// 定义一个类,并且实现Runnable接口
class ThreadTest implements Runnable {
	// 实现Runnable接口中的run()方法
	public void run() {
		System.out.println("我是子线程");
	}
}

Thread类的常用方法:


   getName()     返回该线程的名称

      join()             等待该线程终止

      isAlive()        判断改程序是否处于活动状态

      setName(String name)  设置该线程的名称

      sleep(long time)      使该线程休眠指定的毫秒数   

线程的安全问题

起因:当多个线程同时访问一个对象时,可能会出现线程并发问题。

解决方法:将可能产生并发问题的代码块,加上同步锁,使其在同一个时间只能由一个线程访问。
    synchronized(加锁定的对象){
           可能产生并发问题的代码块;
    }

示例代码:

//模拟卖票的实例
public class Test {
	public static void main(String[] args) {
		// 创建线程对象
		ThreadTest thread = new ThreadTest();
		// 一个Runnable对象可以被多个Thread对象调用
		Thread t1 = new Thread(thread);
		Thread t2 = new Thread(thread);
		Thread t3 = new Thread(thread);
		// 借助Thread对象的start();方法开启线程
		t1.start();
		t2.start();
		t3.start();
	}
}

// 定义一个类,并且实现Runnable接口
class ThreadTest implements Runnable {
	private int tick = 100;

	public void run() {
		while (true) {
			if (tick > 0) {
				try {
					Thread.sleep(20);
				} catch (Exception e) {
				}
				System.out.println(Thread.currentThread().getName() + "    " + tick--);
			}
		}
	}
}

运行以上代码发现,会出现0张票和-1张票等结果,原因就是因为,当其中一个线程通过判断之后,还没来得及执行自减操作,另一个线程就抢夺到了CPU资源,因为这时候另一个线程还没自减,所以此时运行的线程也通过了判断,也就是说最后tick进行了多次自减操作,导致的票数出现0和-1张的情况,解决方案是,将判断语句中的代码加入到同步块中,示例代码:

//模拟卖票的实例
public class Test {
	public static void main(String[] args) {
		// 创建线程对象
		ThreadTest thread = new ThreadTest();
		// 一个Runnable对象可以被多个Thread对象调用
		Thread t1 = new Thread(thread);
		Thread t2 = new Thread(thread);
		Thread t3 = new Thread(thread);
		// 借助Thread对象的start();方法开启线程
		t1.start();
		t2.start();
		t3.start();
	}
}

// 定义一个类,并且实现Runnable接口
class ThreadTest implements Runnable {
	private int tick = 100;

	public void run() {
		while (true) {
			//加入同步代码块,锁定当前对象,及当线程访问到同步代码块时,
			//如果当前对象的锁已经被其他线程持有,那该线程只能在同步代码块外等待。
			synchronized (this) {
				if (tick > 0) {
					try {
						Thread.sleep(20);
					} catch (Exception e) {
					}
					System.out.println(Thread.currentThread().getName() + "    " + tick--);
				}
			}
		}
	}
}

线程的等待与唤醒

线程的等待及调用了wait()方法,唤醒及调用了notify()(唤醒最先等待的线程)或者notifyAll()(唤醒所有等待的线程)方法,两个方法必须在同步代码块中,示例代码:

//生产者与消费者
public class Test {
	public static void main(String[] args) {
		// 创建资源对象
		Resource r = new Resource();
		// 创建生产者对象
		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 Input implements Runnable {
	// 资源对象
	Resource r;

	public Input(Resource r) {
		this.r = r;
	}

	// 生产资源
	public void run() {
		int x = 0;
		while (true) {
			if (x == 0) {
				r.set("dmh", "男");
			} else {
				r.set("hyl", "女");
			}
			x = (x + 1) % 2;
		}
	}
}

// 消费者
class Output implements Runnable {
	// 资源对象
	Resource r;

	public Output(Resource r) {
		this.r = r;
	}

	// 消费资源
	public void run() {
		while (true) {
			r.out();
		}
	}
}

// 资源
class Resource {
	// 姓名
	private String name;
	// 年龄
	private String sex;
	// 线程结束的标识
	private boolean flag = false;

	// 同步方法,锁定的是当前对象
	public synchronized void set(String name, String sex) {
		if (flag)
			try {
				// wait()方法必须在同步代码块中
				// 说明资源正在被消费,当前线程等待
				this.wait();
			} catch (InterruptedException e) {
			}
		this.name = name;
		this.sex = sex;
		flag = true;
		// notify()方法必须在同步代码块中
		// 唤醒等待队列的第一个线程
		this.notify();
	}

	// 同步方法,锁定的是当前对象
	public synchronized void out() {
		if (!flag)
			try {
				// wait()方法必须在同步代码块中
				// 说明资源正在生产,当前线程等待
				this.wait();
			} catch (InterruptedException e) {
			}
		flag = false;
		// notify()方法必须在同步代码块中
		// 唤醒等待队列的第一个线程
		this.notify();
	}
}



     


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值