多线程

1、  java中的多线程

一、概述
首先来了解两名词的概念:进程线程
进程:简单的来说就是正在运行中的程序
线程:就是进程中一个负责程序执行的控制单元(执行路径)
多线程指的是在单个程序中可以同时运行多个同的线程执行不同的任务
多线程的优点:解决了多部分同时运行的问题
多线程的缺点:线程太多导致效率的降低
二、线程状态
如图(该图从网上参考过来):

一个线程可分为五个阶段:

        1、新建状态(New):新创建了一个线程对象。

  2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权

  3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

  4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

  (一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。

  (二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

  (三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

  5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

三、创建线程
java中要想实现多线程,有两种手段:
           第一种方式是继承Thread类,然后重写run方法;
           第二种是实现Runnable接口,然后实现其run方法。 
1、继承Thread

   1)定义一个类继承Thread类。

   (2覆盖Thread类中的run方法。

   (3)直接创建Thread的子类对象创建线程。

   (4)调用start方法开启线程并调用线程的任务run方法执行

其中需要了解的是: start方法与run方法的区别
区别:start方法实现多线程,而run方法没有实现多线程
            start方法 用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到时间片,就开始执行run方法,这里方法run方法称为线程体,它包含了要执行的这个线程的内容,run方法运行结束,此线程随即终止。
            run方法 run方法只是类的一个普通方法而已,如果直接调用run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,并没有实现多线程
总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。
继承Thread多线程实例如下:
public class Test {
	
	public static void main(String[] args) {
		MyThread d1 = new MyThread("线程0");
		MyThread d2 = new MyThread("线程1");
		d1.start();// 开启线程,调用run方法。
		d2.start();
		System.out.println("结束:" + Thread.currentThread().getName());
	}
}
class MyThread extends Thread {
	private String name;
	MyThread(String name) {
		this.name = name;
	}
	public void run() {
		for (int x = 0; x < 1111; x++) {
			System.out.println(name + "....x=" + x + ".....name=" + getName());
		}
	}
}
其中ThreadgetName()获取到线程的名称:Thread-编号(0开始)
主线程的名字为mainThread.currentThread().getName()获取当前线程的名字
输出的部分结果:

2、实现Runnable接口

步骤:

(1)定义类实现Runnable接口

(2)覆盖接口中的run方法,将线程的任务代码封装到run方法中

3通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。

4)调用线程对象的start方法开启线程

public class Test {

	public static void main(String[] args) {
		MyThread d = new MyThread();
		Thread t1 = new Thread(d);
		Thread t2 = new Thread(d);
		t1.start();
		t2.start();
	}
}

class MyThread implements Runnable {

	public void run() {
		for (int x = 0; x < 1111; x++) {
			System.out.println(Thread.currentThread().getName() + "....." + x);
		}
	}
}

实现Runnable接口的好处:(1)避免了java单继承的局限性

四、线程安全问题

线程安全问题产生的原因:

1)多个线程在操作共享的数据

2)操作共享数据的线程代码有多条

当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。

解决思路

        将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程时不可以参与运算的。必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。而java中,同步代码块可以解决这个问题。

同步代码块的格式:

synchronized(对象)
	{
	   //需要被同步的代码 ;
	}
例子:
public class TicketDemo {

	public static void main(String[] args) {
		Ticket t = new Ticket();// 创建一个线程任务对象。
		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 Ticket implements Runnable {
	private int num = 100;
	Object obj = new Object();
	public void run() {
		while (true) {
			synchronized (obj) {
				if (num > 0) {
					try {
						Thread.sleep(10);
						System.out.println(Thread.currentThread().getName()+"----"
								+ num--);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}

同步的好处:解决了线程的安全问题。

同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁

同步的前提:同步中必须有多个线程并使用同一个锁

五、同步的两种方式

1、同步代码块

2、同步函数:使用的锁是this,直接在函数前面加关键字synchronized

同步函数的锁是固定的this,同步代码块的锁是任意的对象,建议使用同步代码块。
public class TicketDemo {

	public static void main(String[] args) {
		Ticket t = new Ticket();// 创建一个线程任务对象。

		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		t1.start();
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
		}
		t.flag = false;
		t2.start();
	}
}

class Ticket implements Runnable {
	private int num = 100;
	boolean flag = true;

	public void run() {
		if (flag)
			while (true) {
				synchronized (this) {
					if (num > 0) {
						try {
							Thread.sleep(10);
							System.out.println(Thread.currentThread().getName()
									+ "...对象..." + num--);
						} catch (InterruptedException e) {
						}

					}
				}
			}
		else
			while (true)
				this.show();
	}

	public synchronized void show() {
		if (num > 0) {
			try {
				Thread.sleep(10);
				System.out.println(Thread.currentThread().getName()
						+ ".....函数...." + num--);
			} catch (InterruptedException e) {
			}

		}
	}
}

单例模式的线程同步:

普通的懒汉式:
class Single {
	private static Single s = null;

	private Single() {
	}

	public static Single getInstance() {
		if (s == null)
			s = new Single();
		return s;
	}
}
当单例模式使用在多线程的条件下:
class Single {
	
	private static Single s = null;
	
	private Single() {
	}
	public static Single getInstance() {
		if (s == null) {
			synchronized (Single.class) // 静态函数同步锁要这样写
			{
				if (s == null)
					s = new Single();
			}
		}
		return s;
	}
}

其中:静态的同步函数使用的锁是:该函数所属字节码文件对象 

           可以用 getClass方法获取,也可以用当前类名.class 表示。

六、线程死锁

常见的死锁:同步的嵌套
public class MyLock {

	public static void main(String[] args) {
		LockTest a = new LockTest(true);
		LockTest b = new LockTest(false);
		Thread t1 = new Thread(a);
		Thread t2 = new Thread(b);
		t1.start();
		t2.start();
	}
}

class Lock {
	public static final Object locka = new Object();
	public static final Object lockb = new Object();
}

class LockTest implements Runnable {
	private boolean flag;

	LockTest(boolean flag) {
		this.flag = flag;
	}

	public void run() {
		if (flag) {
			while (true)
				synchronized (Lock.locka) {
					System.out.println(Thread.currentThread().getName()
							+ "..if   locka....");
					synchronized (Lock.lockb) {
						System.out.println(Thread.currentThread().getName()
								+ "..if   lockb....");
					}
				}
		} else {
			while (true)
				synchronized (Lock.lockb) {
					System.out.println(Thread.currentThread().getName()
							+ "..else  lockb....");
					synchronized (Lock.locka) {
						System.out.println(Thread.currentThread().getName()
								+ "..else   locka....");
					}
				}
		}
	}
}
如上代码:当t1线程启动后,它获得了锁a,后又去申请获得b的锁,而此时a的锁没有放弃

                    当t2线程启动后,它获得了锁b,后又去申请获得a的锁,而此时b的锁没有放弃

                   两方都握有自己的锁不放弃,而同时申请另一方的锁,所以,此时就造成了死锁。

七:其他部分知识点:

waitsleep的区别:

1wait可以指定时间也可以不指定。

      sleep必须指定时间。

2、在同步中时,对cpu的执行权和锁的处理不同。

     wait释放执行权,释放锁

     sleep:释放执行权,不释放锁

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值