程序、进程、线程
程序:是为了完成特定任务,用某种语言编写的一组指令的集合,是一段静态的代码。(程序是静态的)
进程:是程序的一次动态执行。正在运行的一个程序,进程作为资源分配的单位,在内存中会为每个进程分配不同的内存区域。(进程是动态的),进程的生命周期:有它自身的产生、存在和消亡的过程。
线程:进程可进一步细化为线程,是一个程序内部的一条执行路径。若一个进程同一时间并行执行多个线程,就是支持多线程的。
单核CPU与多核CPU
单核CPU:CPU在执行的时候,是按照时间片执行的,同一个时间片只能执行一个任务。执行多个任务时,会在多个任务之间来回跳。时间片切换很快,给人感觉像是在同时执行多个线程。是一种假象。
多核CPU:多个CPU的时候,才真正意义上做到一个时间片多线程同时执行。多线程发挥了最好的效率。真正的多线程。
并行和并发
并行:多个CPU同时执行多个任务。
并发:一个CPU“同时”执行多个任务。(采用时间片切换)
在学习多线程之前,以前的代码都是单线程吗?
不是,以前的代码也是多线程。除了main方法为主线程,还包括处理异常的线程和垃圾回收的线程。PS:处理异常的线程会影响主线程的执行。
创建线程的三种方式
1. 继承Thread类
public class BuyTicketThread extends Thread{
public BuyTicketThread(String name){
super(name);
}
//一共10张票
static int ticketNum = 10;//加上static,使得票数被三个线程共享,否则会售出30张票
@Override
public void run(){
//每个窗口后面有100个人在抢票
for (int i = 1; i <= 100; i++) {
//票数大于0才售票
if (ticketNum>0){
System.out.println("我在"+this.getName()+"抢到了从北京到邯郸的第"+ ticketNum-- +"张票");
}
}
}
}
public class Test {
public static void main(String[] args) {
//多个窗口抢票
BuyTicketThread buy1 = new BuyTicketThread("窗口1");
buy1.start();
BuyTicketThread buy2 = new BuyTicketThread("窗口2");
buy2.start();
BuyTicketThread buy3 = new BuyTicketThread("窗口3");
buy3.start();
}
}
2.实现Runnable接口
public class BuyTicketThread implements Runnable{
int ticketNum = 10;
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
if (ticketNum > 0){
System.out.println("我在"+Thread.currentThread().getName()+"买到了第"+ ticketNum-- +"张北京到邯郸的车票");
}
}
}
}
public class Test {
public static void main(String[] args) {
//定义一个线程对象
BuyTicketThread buy = new BuyTicketThread();
//窗口1买票
Thread thread1 = new Thread(buy,"窗口1");
thread1.start();
//窗口2买票
Thread thread2 = new Thread(buy,"窗口2");
thread2.start();
//窗口3买票
Thread thread3 = new Thread(buy,"窗口3");
thread3.start();
}
}
3.实现Callable接口
对比方式1和方式2,都需要有一个run方法,但是这个run方法有不足之处:没有返回值,不能抛出异常。
基于上面两个不足,在JDK1.5之后出现了第三种创建多线程的方式:实现Callable接口:有返回值,可以抛出异常,但是创建比较麻烦。
public class TestCallable implements Callable<Integer> {
/**
* 实现Callable接口可以不带泛型,如果不带则返回值是object类型
* @return
* @throws Exception
*/
@Override
public Integer call() throws Exception {
return new Random().nextInt(10);//返回10以内的随机数
}
}
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//定义一个线程对象
TestCallable tc = new TestCallable();
FutureTask ft = new FutureTask(tc);
Thread thread = new Thread(ft);
thread.start();
//获取线程的返回值
Object obj = ft.get();
System.out.println(obj);
}
}
方式1继承Thread类和方式2实现Runnable接口,实际开发中哪个方式更好?
方式1在Java单继承中具有局限性,继承了Thread类,就不能继承其他类了。
方式2的共享资源能力更强一些,变量不用加static来修饰也可以实现共享,方式1共享变量必须用static修饰。(为什么不用static也可以)
Thread类和Runnable接口有什么关系?
Runnable接口--实现-->Thread类,即Thread类实现了Runnable接口。(方式2的线程类实现了Runnable接口,方式1的线程类继承了Thread类。)
线程的生命周期
新生状态、就绪状态、执行状态、死亡状态、阻塞状态。
Thread thread = new Thread();新生状态-----start启动----》就绪状态(等待CPU调度)----run()获取cpu执行权--------》运行状态-------正常结束/出现异常/调用stop方法(不建议,该方法已废弃)----------》死亡状态
Thread thread = new Thread();新生状态-----start启动----》就绪状态(等待CPU调度)----run()获取cpu执行权--------》运行状态-------阻塞事件----------》阻塞状态-------阻塞事件解除----------------》就绪状态
线程的常见方法
start():启动当前线程,表面上调用start方法,实际上执行run方法。
run():线程类继承Thread或者实现runnable接口的时候,都要重写run方法,run方法里面是线程要执行的内容。
currentThread:Thread类中的一个静态方法,获取正在执行的线程。
setName():设置线程名称
getName():获取线程名称
setPriority(int num):设置优先级别方法:同优先级别的线程采取先到先服务,使用时间片策略。如果优先级高,被CPU调度的概率就高。级别为:1-10,默认是5。
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
设置优先级别
public class TestThread01 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}
public class TestThread02 extends Thread{
@Override
public void run() {
for (int i = 20; i < 30; i++) {
System.out.println(i);
}
}
}
public class Test {
public static void main(String[] args) {
TestThread01 thread01 = new TestThread01();
thread01.setPriority(10);//优先级别高
thread01.start();
TestThread02 thread02 = new TestThread02();
thread02.setPriority(2);//优先级别低
thread02.start();
}
}
join方法
当一个线程调用了join方法,这个线程就会先被执行,它执行结束以后才可以去执行其余的线程。注意:必须先start,再join才有效。
public class TestThreadJoin extends Thread{
public TestThreadJoin(String name){
super(name);
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(this.getName()+"----------"+i);
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 100; i++) {
System.out.println("main-------------"+i);
if (i==6){
//创建子线程
TestThreadJoin ttj = new TestThreadJoin("join子线程");
ttj.start();
ttj.join();//等子线程执行完再执行主线程。子线程打印0-9
}
}
}
}
sleep方法
sleep人为制造阻塞事件
public class Test {
public static void main(String[] args) throws InterruptedException {
DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
Date date = new Date();
String format = dateFormat.format(date);
System.out.println(format);
Thread.sleep(3000);
}
}
设置伴随线程
将子线程设置为主线程的伴随线程,主线程停止的时候,子线程也不要继续执行了。不会立马结束,会垂死挣扎一会。
public class TestThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("子线程--"+i);
}
}
}
class Test{
public static void main(String[] args) {
//创建并启动子线程
TestThread thread = new TestThread();
thread.setDaemon(true);//设置伴随线程,先设置,再启动
thread.start();
//主线程输出1到10
for (int i = 0; i < 10; i++) {
System.out.println("main---"+i);
}
}
}
stop方法
//终止当前线程。
public class TestStop{
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
if (i==6){
Thread.currentThread().stop();//过期方法,不建议使用
}
System.out.println(i);
}
}
}
标签:Java,Thread,笔记,start,static,线程,new,多线程,public
From: https://www.cnblogs.com/shaokai7878/p/16950796.html