Java学习之多线程分析

要学习多线程,需要了解什么是"线程"?要了解线程,首先先了解一下什么是"进程"?
线程在游戏中的应用体现是最为明显的,当你使用一个账号登录时,在进行游戏的过程中,我们游戏角色的生死存亡就是挂在一个线程上。如果是攻城式的游戏,即自带小兵攻击,我们经常玩游戏的应该有感受,当你被消灭时,你的小兵也会跟着死掉,这其实就是在后面会提到的daemon守护线程的操作,这个会在后面展现出来!来来,入门伸头看看咯!

什么是进程?一般来说一个应用程序开始运行,它就是一个主"进程"。它由操作系统管理;每一个进程都有它自己的内存空间和系统资源。
什么是线程?线程,是由某个进程单独启动的一个独立运行代码块。一个线程,一旦启动之后,同主进程一样,一起竞争系统资源。一个进程,可以开出很多的线程。一个线程就是一个单独的执行路径。多个线程,就是多个执行路径,所有的这些执行路径同时竞争系统资源;
单线程和多线程:
1).单线程:一个应用程序只有一条执行路径。(之前我们开发的所有程序)
2).多线程:一个应用程序开出多个线程,也就是具有多个执行路径。
Java程序运行的原理:
1).由虚拟机加载启动的main()方法所在类。
2).同时,虚拟机在后台也会运行一些其他的程序:例如:垃圾回收器。
3).java虚拟机也是一个多线程的程序;
什么是并行和并发?
前者是逻辑上同时发生,指在"某一个时间内"同时运行多个程序。
后者是物理上同时发生,指在"某一个时间点"同时运行多个程序。
那么,我们能不能实现真正意义上的并发呢,是可以的,多个CPU就可以实现,不过你得知道如何调度和控制它们。

/*

  • 线程的实现方式一:
  • 步骤:
  • 1.定义一个类,继承自java.lang.Thread
  • 2.重写里面的run()方法;
  • 3.在测试类中,实例化自定义类的对象,并调用它的start()方法,启动一个线程;
  • 线程的实现方式二:
  • 步骤:
  • 1.定义一个类,实现Runnable接口;
  • 2.重写里面的run()方法;
  • 3.在测试类中:
  • 1).实例化自定义类的对象;
  • 2).Thread t = new Thread(自定义类对象引用);
  •  t.start();
    
  •  简化的写法:
    
  •  new Thread(自定义类对象引用).start( );
    
package com.myTest.demo02_线程的实现方式一;
public class Demo {
	public static void main(String[] args) {
		//3.在测试类中,实例化自定义类的对象,并调用它的start()方法,启动一个线程;
		MyThread t = new MyThread();
		t.start();//启动线程,会执行run()方法
	//	t.run();//普通的方法调用,并不能作为一个线程启动。
	//	t.start();//对于同一个线程对象,不能多次的start();异常:IllegalThreadStateException
		for(int j = 0;j < 20 ;j++){
			System.out.println("j = " + j);
		}
		System.out.println("当前main()方法 的线程名称:" + 
Thread.currentThread().getName());
		System.out.println("main方法执行完毕!");
	}
}

线程的实现方式一;

//1.自定义类,继承自Thread。代表我们的类就是一个Thread
public class MyThread extends Thread{

//2.重写Thread中的run()方法;在线程启动后,需要做的事情,写在run()方法中;
	@Override
	public void run() {
		for(int i = 0;i < 20;i++){
			System.out.println("i = " + i);
		}
	}
}

线程的调度_sleep 休眠;

线程的休眠:Thread --> public static void sleep(long millis)

import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo {
	public static void main(String[] args) {
		/*for(int i = 1 ;i <= 10;i++){
			//每秒打印一次
			Date date = new Date();
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			String dateStr = sdf.format(date);
			System.out.println("当前时间: " + dateStr);
			//休息1秒
			try {
				//当前线程休眠
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}*/
		
		//启动线程
		MyThread t = new MyThread();
		t.start();
	}
}


import java.text.SimpleDateFormat;
import java.util.Date;

public class MyThread extends Thread {
	@Override
	public void run() {
		for(int i = 1 ;i <= 10;i++){
			//每秒打印一次
			Date date = new Date();
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			String dateStr = sdf.format(date);
			System.out.println("当前时间: " + dateStr);
			//休息1秒
			try {
				//当前线程休眠
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}	

默认情况下,当前进程开出的所有线程都是"非守护线程":当主进程结束,会等待所有线程结束,应用程序才会结束;

让开出的线程作为"守护线程":Thread -->public final void setDaemon(boolean on):如果为true,为守护线程

守护线程:当主进程结束时,所有的"守护线程"也会结束。(但不会立即结束,通常会有个小缓冲)

后台线程_守护线程_setDaemon;

public class Demo {
	public static void main(String[] args) {
		MyThread t1 = new MyThread();
		MyThread t2 = new MyThread();
		
		t1.setName("兵1");
		t2.setName("兵2");
		
		t1.setDaemon(true);
		t2.setDaemon(true);
		
		t1.start();
		t2.start();
		
		//主进程是将军
		for(int i = 1 ;i <= 10;i++){
			System.out.println("将军杀敌:" + i);
		}
		System.out.println("将军杀敌结束,收工!!");
	}
}


public class MyThread extends Thread{
	@Override
	public void run() {
		System.out.println("我是:" + this.getName() + " ,我要杀敌100.");
		for(int i = 0;i < 100;i++){
			System.out.println(this.getName() + " 杀敌:" + (i + 1));
		}
		System.out.println(this.getName() + " 杀完了!收工!!");
	}
}

停止线程_interrupt;

  • 停止线程:
  • public final void stop():
    public void interrupt():
public class Demo {
	public static void main(String[] args) {
		//启动MyThread,MyThread中的run会执行20秒,我们可以在主进程中,让
		//MyThread执行3秒后,如果没有停止,那么就强行停止它
		MyThread t = new MyThread();
		t.start();
		
		/*try {
			Thread.sleep(1000 * 3);//等待3秒,MyThread会执行3秒
		} catch (InterruptedException e) {
			e.printStackTrace();
		}*/
		//强行去停止MyThread
	//	t.stop();//停止线程,但此方法已过时;
		t.interrupt();//stop的替换方法;去使MyThread的run()抛出一个异常,在异常中,我们去结束线程执行;
		
	}
}

public class MyThread extends Thread{
	@Override
	public void run() {
		
		for(int i = 0;i < 20 ;i++){
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
				System.out.println("线程抛出异常,出现意外情况了,我结束执行!");
				break;
			}
			System.out.println("i = " + i);
		}
		
	}
}

  • 线程的实现方式二:
  • 步骤:
  • 1.定义一个类,实现Runnable接口;
  • 2.重写里面的run()方法;
  • 3.在测试类中:
  • 1).实例化自定义类的对象;
  • 2).Thread t = new Thread(自定义类对象引用);
  •  t.start();
    
  •  简化的写法:
    
  •  new Thread(自定义类对象引用).start();
    
public class Demo {
	public static void main(String[] args) {
		//3.1.实例化
		MyRunnable myRun = new MyRunnable();
		//3.2
		/*Thread t1 = new Thread(myRun,"邓超");
		Thread t2 = new Thread(myRun,"孙俪");
		//启动线程
		t1.start();
		t2.start();*/
		//简化写法
		new Thread(myRun,"邓超").start();
		new Thread(myRun,"孙俪").start();
	}
}


public class MyRunnable implements Runnable{

	@Override
	public void run() {
		for(int i = 0;i < 20;i++){
			System.out.println(Thread.currentThread().getName() + " i = " + i);
		}
	}

}




Runnable接口实现卖票程序;

public class Demo {
	public static void main(String[] args) {
		TicketPool pool = new TicketPool();
		
		SellTicketRunnable tRun1 = new SellTicketRunnable(pool);
		SellTicketRunnable tRun2 = new SellTicketRunnable(pool);
		SellTicketRunnable tRun3 = new SellTicketRunnable(pool);
		
		new Thread(tRun1,"窗口1").start();
		new Thread(tRun2,"窗口2").start();
		new Thread(tRun3,"窗口3").start();
	}
}


import java.util.ArrayList;

public class SellTicketRunnable implements Runnable {
	private TicketPool pool = null;
	//ArrayList不保证线程安全
	private ArrayList<Integer> ticArray = new ArrayList<Integer>();

	public SellTicketRunnable(TicketPool pool) {
		this.pool = pool;

	}

	@Override
	public void run() {
		// 一直抢票,一直抢到返回值为0
		while (true) {
			int t = this.pool.getTicket();
			if (t <= 0) {
				System.out.println("没票了,我结束了!");
				break;
			}
			// System.out.println(this.getName() + " 抢到票:" + t);
			ticArray.add(t);// 将票装到集合中
		}
		System.out.println(Thread.currentThread().getName() + " 一共抢到:" + this.ticArray.size()
				+ " 张票!");
	}

}

public class TicketPool {
	private int tickets = 100;
	
	public int getTicket(){
		if(tickets > 0){
			return this.tickets--;
		}else{
			return 0;
		}
	}
}

进行同步操作后
加入同步代码块_synchronized;
/*

  • 同步代码块,解决多线程环境下的并发问题:
  • 问题:
  • 1.是否是多线程环境
    2.是否有共享数据
    3.是否有多条语句操作共享数据

解决:将可能会被多个线程访问的代码进行"同步"使用关键字:synchronized
语法:synchronized(theObject){
//可能会被多个线程访问的代码
}
theObject:是一个对象,对其加锁的对象。表示,当我访问这个对象的某个同步代码块时,
其它的线程不能访问这个对象的同步方法或同步代码块
synchronized:
好处:代码在多线程环境下变得安全,数据变得可靠;
弊端:由于内部做了很多线程控制方面的工作,所以效率低。

public class Demo {
	public static void main(String[] args) {
		TicketPool pool = new TicketPool();
		
		SellTicketThread t1 = new SellTicketThread(pool);
		SellTicketThread t2 = new SellTicketThread(pool);
		SellTicketThread t3 = new SellTicketThread(pool);
		
		t1.setName("窗口1");
		t2.setName("窗口2");
		t3.setName("窗口3");
		
		t1.start();
		t2.start();
		t3.start();
	}
}


import java.util.ArrayList;

public class SellTicketThread extends Thread {
	private TicketPool tPool = null;
	private ArrayList<Integer> ticArray = new ArrayList<Integer>();
	
	public SellTicketThread(TicketPool pool){
		this.tPool = pool;
	}
	@Override
	public void run() {
		//一直抢票,一直抢到返回值为0
		while(true){
			int t = this.tPool.getTicket();
			if(t <= 0){
				System.out.println("没票了,我结束了!");
				break;
			}
		//	System.out.println(this.getName() + " 抢到票:" + t);
			ticArray.add(t);//将票装到集合中
		}
		System.out.println(this.getName() + " 一共抢到:" + this.ticArray.size() + " 张票!");
	}
}


public class TicketPool {
	private int tickets = 100;
	
	public int getTicket(){//窗口1,窗口2
		//比如这里有些复杂的操作,需要耗费5毫秒
		synchronized(this){//下面这段代码具有多线程的安全性:窗口1 先进入
			try {//这段代码块将会被锁定,不允许其它线程访问。如果有线程访问,将会列队等待,直到窗口1执行完毕;
				Thread.sleep(5);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if(tickets > 0){
				return this.tickets--;
			}else{
				return 0;
			}
		}
		//窗口1 执行完毕,解锁,其它线程才可以进入
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值