一,进程与线程
- 进程是资源分配的最小单位,线程是资源调度(程序执行)的最小单位
- 进程指一次程序的完整运行,一个进程包含多个线程,线程比进程处理快,线程的存在离不开进程,一个线程终止整个进程将终止,而进程之间不会相互影响
- 例如打开微信,就相当于启动一个进程,打开朋友圈,微信运动等相当于执行线程
- 再例如,进程相当于火车,线程相当于车厢
二,多线程的实现方法
- 实现多线程的两种方式:继承Thread类,继承Runnable接口
- Thread 类start()方法用于启动多线程,自动调用run()方法(这是由jvm所决定的),run()地位相当于main(),都是程序的入口,run()是线程程序的入口,main()是主程序的入口。
- 启动顺序并不等于执行顺序,先start()不一定先run()
- Runnable接口 没有start()方法,但可以使用 public Thread(Runnable target){},调用该接口的子类对象
package 进程与线程;
class MyThread extends Thread{
private String str;
public MyThread(String str)
{this.str=str;}
//覆写Run方法
@Override
public void run() {
for (int i = 0; i <200 ; i++) {
System.out.println(this.str+"--->"+i);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread m1 = new MyThread("线程1");
MyThread m2 = new MyThread("线程2");
MyThread m3 = new MyThread("线程3");
m1.start();
m2.start();
m3.start();
}
}
package 进程与线程;
class MyThread implements Runnable{
private String str;
public MyThread(String str)
{this.str=str;}
//覆写Run方法
@Override
public void run() {
for (int i = 0; i <200 ; i++) {
System.out.println(this.str+"--->"+i);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread m1 = new MyThread("线程1");
MyThread m2 = new MyThread("线程2");
MyThread m3 = new MyThread("线程3");
new Thread(m1).start();
new Thread(m2).start();
new Thread(m3).start();
}
}
- Thread类是Runnable的子类,接口实现了多继承
三,进程与线程的比较
- 多个线程对象访问同一资源用Runnable接口,实现资源共享(车厢a换到车厢b),而进程之间很难实现资源共享
package 进程与线程;
class MyThread extends Thread{
private int ticket = 10;
//覆写Run方法
@Override
public void run() {
for (int i = 0; i <50 ; i++) {
if(ticket>0)
System.out.println("剩余票数:"+"--->"+ticket--);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread m1 = new MyThread();
MyThread m2 = new MyThread();
MyThread m3 = new MyThread();
m1.start();
m2.start();
m3.start();
}
}
- 实例化三个不同的线程对象,每个线程对象对各自的ticket进行操作,没有实现资源共享
package 进程与线程;
class MyThread implements Runnable{
private int ticket = 10;
//覆写Run方法
@Override
public void run() {
for (int i = 0; i <50 ; i++) {
if(ticket>0)
System.out.println("剩余票数:"+"--->"+ticket--);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread m1 = new MyThread();
new Thread(m1).start();
new Thread(m1).start();
new Thread(m1).start();
}
}
- 实例化三个Thread类进程对象,每个进程对象对应同一个MyThread子类对象,可以实现资源共享
- 关于ticket输出不是10,9,8,7,6,5,4,3,2,1,0,以为ticket不是原子操作,受到线程的影响,可能先println,再进行ticket- -,或者先进行ticket- -再进行println <可以这样理解,卖出票时要记下剩余票数并报出(println),许多票贩分布在大街小巷卖票a卖出了一张票(ticket - -),b卖出了一张票(ticket - -),这时b先当时记下报出剩余票数,a后报出当时记下的剩余票数>
四,多线程的同步问题:
1.问题的引出:多个线程访问同一资源,例如多个票贩卖同一张票,票数出现负数的情况。
package 进程与线程;
class MyThread implements Runnable{
private int ticket = 10;
//覆写Run方法
@Override
public void run() {
for (int i = 0; i <20 ; i++) {
if(ticket>=0)
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--->"+ticket--);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread m1 = new MyThread();
new Thread(m1,"票贩子a").start();
new Thread(m1,"票贩子b").start();
new Thread(m1,"票贩子c").start();
}
}
- 第一个线程执行并休眠时,没有进行ticket - - ,当前票数仍为1,所以第二个线程执行时,通过了剩余票数的判断,最后进行修改剩余票数时,会出现负数的情况。
2.解决方法:使用synchronized(){},进行上锁,以下两种方法都是为了保证同一时间只有一个线程被执行,其他线程必须等待该线程执行完成才能执行,即同一时间只有一个票贩卖票。
- 同步代码块
package 进程与线程;
class MyThread implements Runnable{
private int ticket = 10;
//覆写Run方法
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
synchronized (this) {//当前操作只允许一个线程对象进入
if (this.ticket >= 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->" + this.ticket--);
}
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread m1 = new MyThread();
new Thread(m1,"票贩子a").start();
new Thread(m1,"票贩子b").start();
new Thread(m1,"票贩子c").start();
}
}
- 同步函数
package 进程与线程;
class MyThread implements Runnable{
private int ticket = 10;
//覆写Run方法
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
this.sale();
}
}
public synchronized void sale()
{
if (this.ticket >= 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->" + this.ticket--);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread m1 = new MyThread();
new Thread(m1,"票贩子a").start();
new Thread(m1,"票贩子b").start();
new Thread(m1,"票贩子c").start();
}
}
- 将判断剩余票数,休眠,修改剩余票数用synchronized进行封装上锁,使得同一时间只能有一个线程对象执行这个完整操作
- 同步操作与异步操作相比,执行速度低于异步操作,但同步操作的安全性高。
五,多线程经典模型-----生产者与消费者:生产一个,取走一个进行输出,不存在连续重复输出同一个产品,而且产品的特点不会产生错乱的情况,实现了线程有序交替执行。
package 进程与线程;
class Info
{
private String title;
private String content;
private boolean flag = true;
//flag=true,可以生产不可以取走,flag=flase 可以取走不可以生产
public synchronized void set(String title,String content) throws InterruptedException {
if(this.flag==false)//重复进入set(),发现不可以生产,进行等待
{
super.wait();
}
this.title=title;
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.content=content;
this.flag=false;//生产完后进行标记修改
super.notify();//唤醒其他等待取出数据线程
}
public synchronized void get() throws InterruptedException {
if(flag==true)//重复进入get(),数据正在生产中,发现取不到数据,进行等待
{
super.wait();
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.title+"---->"+this.content);
this.flag=true;//取走数据,进行标记的修改
super.notify();//唤醒其他等待生产数据线程
}
}
class Productor implements Runnable{
private Info info;
public Productor(Info info)
{this.info=info;}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
if(i%2==0) {
try {
this.info.set("生产油桃", "饱满多汁");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else {
try {
this.info.set("生产哈密瓜","香甜可口");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class Customer implements Runnable {
private Info info;
public Customer(Info info) {
this.info = info;
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
try {
this.info.get();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
Info info = new Info();
new Thread(new Productor(info)).start();
new Thread(new Customer(info)).start();
}
}
- sleep()与wait()区别,sleep()是Thread类定义的方法,wait()是Object类定义的方法
sleep()可以设置休眠时间,时间一到自动唤醒,wait()休眠需要notify()进行唤醒