Java基础---线程

进程:正在运行的应用程序称作为一个进程, 负责内存空间的划分
线程:进程中的一个执行序列,负责进程中的代码执行,轻量级进程
多线程:在一个进程中有着多个线程,同时执行多个任务代码
多线程好处:
        1. 解决了在一个进程中可以同时执行多个任务代码的问题。
        2. 提高资源的利用率
多线程弊端:
        1. 增加了cpu的负担
        2. 降低了一个进程中线程的执行概率。
        3. 引发了线程安全问题。
        4. 引发了死锁现象。


 线程执行过程中的状态

1

就绪(Runnable):

线程准备运行,不一定立马就能开始执行。

2

运行中(Running):

进程正在执行线程的代码

3

等待中(Waiting):

线程处于阻塞的状态,等待外部的处理结束

4

睡眠中(Sleeping):

线程被强制睡眠。

5

I/O 阻塞(Blocked on I/O):

等待 I/O 操作完成。

6

同步阻塞(Blocked on Synchronization):

等待获取锁。

7

死亡(Dead):

线程完成了执行


自定义线程的步骤:
    方式一:继承类
            1. 自定义一个类继承Thread
            2. 重写run方法,把自定义线程的任务定义在run方法中。
            3. 创建Thread子类的对象,然后调用start方法开启线程。

//方式一:继承Thread类
public class Thread1 extends Thread {

	@Override
	public void run() {
		// 自定义线程的任务代码
		for (int i = 0; i < 100; i++) {
			System.out.println("自定义线程:" + i);
		}
	}

	public static void main(String[] args) {
		// 创建线程对象
		Thread1 td1 = new Thread1();
		// 开启线程
		td1.start();
		for (int i = 0; i < 100; i++) {
			System.out.println("主线程(main):" + i);
		}
	}
}

 


  方式二:实现runable接口
            1. 自定义一个类去实现Runnable接口。
             2. 实现了Runnable接口的run方法, 把自定义线程的任务定义在run方法上。
             3. 创建Runnable实现类的对象。
             4. 创建Thread对象,并且把Runnable实现类对象作为参数传递进去。
             5. 调用thread对象的start方法开启线程。

//方式二:实现Runnable接口
public class Runnable1 implements Runnable {

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName() + ":" + i);
		}
		// 当前线程对象是:
		System.out.println("当前线程对象:" + Thread.currentThread());
	}

	public static void main(String[] args) {
		// 创建Runnable实现类的对象
		Runnable1 d = new Runnable1();
		// 创建Thread对象,并且把Runnable实现类对象作为参数传递进去
		Thread t = new Thread(d, "奥特曼");
		// 调用thead对象的start方法开启线程。
		// 调用了Runanble实现类对象的run方法。 就是相当于把Runnable实现类的run方法作为了线程的任务代码去执行
		t.start();

		// 主线程执行的。
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName() + ":" + i);
		}
		System.out.println("当前线程对象:" + Thread.currentThread());
	}

}

注意:
      1.不能直接调用run方法,直接调用run相当于调用了一个普通的方法
      2.必须调用start()方法,run()自动调用
      3.Runnable实现类的对象并不是一个线程对象,只是实现了Runnable接口的对象
      4.把Runnable实现类的对象作为参数传递给thread对象是为了执行Runnable实现类的对象的run方法
重写run方法目的:自定义线程的任务代码就是run方法中的所有代码
                               main线程的任务代码是main方法里面的所有代码


方式三:Executor 框架来创建线程池


线程常用方法:

Thread(String name)

初始化线程的名字

setName(String name)

设置线程对象名

getName()

返回线程的名字

static sleep()

线程睡眠指定毫秒数

 

currentThread()

返回当前执行该方法的线程对象

在哪个线程里就返回那个线程

getPriority()

返回当前线程对象的优先级  默认5

setPriority(int x)

设置线程的优先级  (0--10)

public class Thread3 extends Thread {

	public Thread3(String name) {
		// 调用Thread类一个参数的构造方法
		// 初始化线程名字
		super(name);
	}

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

	}

	public static void main(String[] args) throws InterruptedException {
		// 创建一个自定义的线程对象
		Thread3 td = new Thread3("奥特曼");
		// 设置线程优先级
		td.setPriority(1);
		// 获取线程优先级
		Thread.sleep(3000);
		System.out.println("自定义线程的优先级:" + td.getPriority());
		// 开启线程
		td.start();
		// 指定线程睡眠的毫秒数 沉睡的是main主线程
		Thread.sleep(3000);
		// 返回当前线程
		Thread mainThread = Thread.currentThread();
		System.out.println(Thread.currentThread());
		// 默认的优先级是5
		System.out.println("主线程的优先级:" + mainThread.getPriority());
		// 设置线程的优先级 优先级越高的线程得到cpu的概率越大。 优先级的范围:1~10
		mainThread.setPriority(10);
		System.out.println("主线程的名字:" + mainThread.getName());
		for (int i = 0; i < 100; i++) {
			System.out.println(mainThread.getName() + ":" + i);
		}

	}
}

线程安全问题根本原因:
    1. 存在两个或者两个以上的线程。
    2. 多个线程共享着一个资源,而且操作资源的代码有多句。
线程安全问题解决: 
     方式一:同步代码块
         格式:
             synchronized(锁对象){
                 需要被同步的代码
             }        
     注意事项:
         1. 锁对象可以是任意的一个对象。
         2. 锁对象必须是多个 线程共享 的资源。
         3. 调用了sleep方法的线程并不会释放锁对象
         4. 不存在着线程安全问题时,不要使用同步代码块或者是同步函数

//三个窗口卖票,同步代码块解决
class SaleTickets extends Thread {
	// 非静态成员变量。 非静态成员变量在每个对象中都维护了一份数据。
	// 使用static修饰票数num,让该数据共享出来给所有的对象使用
	static int num = 50;
	// 定义一个锁对象 共享
	static Object o = new Object();
	// 调用父类一个参数的构造函数, 初始化线程的名字。
	public SaleTickets(String name) {
		super(name);
	}
	// 线程的任务代码...使用同步代码块
	@Override
	public void run() {
		while (true) {
			// synchronized ("锁") {
			synchronized (o) {
				if (num > 0) {
					try {
						Thread.sleep(1000);
						System.out.println(Thread.currentThread().getName() + "卖出了" + num + "号票");
						num--;
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				} else {
					System.out.println("售罄了...");
					break;
				}
			}
		}
	}
}

public class Thread4 extends Thread {

	public static void main(String[] args) {
		// 创建线程对象
		SaleTickets thread1 = new SaleTickets("窗口1");
		SaleTickets thread2 = new SaleTickets("窗口2");
		SaleTickets thread3 = new SaleTickets("窗口3");
		// 开启线程
		thread1.start();
		thread2.start();
		thread3.start();
	}
}
//同步代码块,使用runnable接口解决
class SaleTickets implements Runnable {
	// 非静态成员变量
	int num = 50;

	@Override
	public void run() {
		while (true) {
			synchronized ("锁") {
				if (num > 0) {
					System.out.println(Thread.currentThread().getName() + "卖出了第" + num + "号票");
					num--;
				} else {
					System.out.println("售罄了...");
					break;
				}
			}
		}
	}
}
public class Runnable2 {

	public static void main(String[] args) {
		// 创建Runnable实现类的对象
		SaleTickets saleTickets = new SaleTickets();
		// 创建三个线程对象
		Thread t1 = new Thread(saleTickets, "窗口1");
		Thread t2 = new Thread(saleTickets, "窗口2");
		Thread t3 = new Thread(saleTickets, "窗口3");
		// 调用start方法开启线程
		t1.start();
		t2.start();
		t3.start();
	}
}

 方式二:同步函数  : 使用synchronized修饰的函数
     注意事项:

         非静态同步函数的锁对象是this对象,静态函数的锁对象是当前所属类的class文件对象。 

  • 推荐使用: 同步代码块
  • 推荐原因:
  •          1. 同步代码块的锁对象可以自己指定,同步函数的锁对象是固定的。
  •          2. 同步代码块可以随意指定范围需要被同步,同步函数必须是整个函数都同步

       每个人都拥有其他人需要的资源,同时又等待其他人拥有的资源,并且每个人在获得所有需要的资源之前都不会放弃已经拥有的资源
死锁现象出现 的根本原因:
         1. 存在两个或者两个以上的线程存在。
         2. 多个线程必须共享两个或者两个以上的资源却释放。
没法解决,只能避免!

  • 简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。

class DeadLockThread extends Thread {
	public DeadLockThread(String name) {
		super(name);
	}
	@Override
	public void run() {
		if ("张三".equals(this.getName())) {
			synchronized ("遥控器") {
				System.out.println(this.getName() + "取走了遥控器,准备取电池");
				synchronized ("电池") {
					System.out.println(this.getName() + "取到了电池,开着空调爽歪歪的吹着 !!");
				}
			}

		} else if ("李四".equals(this.getName())) {
			synchronized ("电池") {
				System.out.println(this.getName() + "取走了电池,准备取取遥控器");
				synchronized ("遥控器") {
					System.out.println(this.getName() + "取走了遥控器,开着空调爽歪歪的吹着 !!");
				}
			}
		}
	}

}
public class DeadLock {
	public static void main(String[] args) {
		// 创建了线程对象
		DeadLockThread thread1 = new DeadLockThread("张三");
		DeadLockThread thread2 = new DeadLockThread("李四");
		thread1.setPriority(10);
		thread2.setPriority(1);
		// 调用start方法启动线程
		thread1.start();
		thread2.start();

	}
}

线程通讯
    方法:
        wait()           该线程进入以锁对象建立的线程池中暂停并等待执行
        notify()        该线程会唤醒线程池中一个等待线程中
        notifyAll();  唤醒所有的线程

    注意:
        1. wait、notify、notifyAll方法都是属于Object对象方法
        2. wait、notify、 notifyAll方法必须要在同步代码块或者是同步函数中调用。
        3. wait、notify、 notifyAll方法必须由锁对象调用
        4. 一个线程执行了wait方法会释放锁对象

//线程通讯:生产者生产一个消费者消费一个
//产品类
class Product {
	String name;
	int price;
	// 产品是否生成完毕的标识     false为还没有生成完毕, true 生成完毕了.
	boolean flag; 
}
// 生产者类
class Producer extends Thread {
	// 维护一个产品
	Product p;
	public Producer(Product p) {
		this.p = p;
	}

	@Override
	public void run() {
		int i = 0;
		while (true) {
			synchronized (p) {
				if (p.flag == false) {
					if (i % 2 == 0) {
						p.name = "摩托车";
						p.price = 4000;
					} else {
						p.name = "自行车";
						p.price = 300;
					}
					System.out.println("生产了" + p.name + " 价格:" + p.price);
					i++;
					// 生成完毕 --- 改标识
					p.flag = true;
					// 唤醒消费者去消费
					p.notifyAll();
					;
				} else {
					// 如果产品已经生产完毕,应该等待消费者先消费
					try {
						p.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}

// 消费者
class Customer extends Thread {
	// 维护一个产品类
	Product p;
	public Customer(Product p) {
		this.p = p;
	}

	@Override
	public void run() {
		while (true) {
			synchronized (p) {
				if (p.flag == true) {
					System.out.println("消费者消费了:" + p.name + " 价格:" + p.price);
					// 改标识
					p.flag = false;
					p.notifyAll();
				} else {
					// 如果产品已经被消费完毕,应该唤醒生产者去生成
					try {
						p.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}

public class Thread8 {
	public static void main(String[] args) {
		// 创建一个产品对象
		Product p = new Product();
		// 创建线程对象
		Producer producer = new Producer(p);
		Customer customer = new Customer(p);
		// 启动线程
		producer.start();
		customer.start();
	}
}

守护线程(后台线程)
        d.setDaemon(true)        设置线程为守护线程
        当前一个java应用只剩下守护线程的时候,那么守护线程马上结束。
        守护线程应用场景:新的软件版本下载

//守护线程应用场景
//模拟QQ在下载更新包
public class Thread6 extends Thread {

	public Thread6(String name) {
		super(name);
	}

	@Override
	public void run() {
		// 子类抛出的异常类型必须要小于或者等于父类抛出 的异常类型。
		for (int i = 1; i < 100; i++) {
			System.out.println(this.getName() + "已经下载了:" + i + "%");
		}
		System.out.println("下载完毕,正在安装更新包!!!");
	}

	public static void main(String[] args) {
		// 创建一个线程对象
		Thread6 d = new Thread6("守护线程");
		// 设置一个线程为守护线程
		d.setDaemon(true);
		// 判断一个线程是否为守护线程。
		System.out.println("是守护线程吗?" + d.isDaemon());
		// 启动线程
		d.start();
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName() + ":" + i);
		}
	}//只剩守护线程时,守护线程会立即结束

}

线程让步:join()方法 

当前线程执行join方法,那当前线程就会让步给新线程先完成任务,然后继续的执行自己的任务

停止线程:interrupt() 
        注意:
            1. 我们停止一个线程一般都会配合一个变量去控制。
            2. 如果停止的是一个等待状态下的线程,那么需要配合interrupt方法使用。 

public class Thread9 extends Thread {
	boolean flag = true;

	public Thread9(String name) {
		super(name);
	}

	@Override
	public synchronized void run() {
		int i = 0;
		while (flag) {
			System.out.println(Thread.currentThread().getName() + ":" + i);
			i++;
		}
	}

	public static void main(String[] args) {
		// 创建线程对象
		Thread9 d = new Thread9("奥特曼");
		d.start();

		// 当主线程的i到50的时候,停止奥特曼线程。
		for (int i = 0; i < 100; i++) {
			if (i == 80) {
                // interrupt() 无法停止一个线程,
				d.flag = false; 
                // 强制清除一个线程的wait、 sleep状态。 可以指定清除那个线程。
				d.interrupt(); 
			}
			System.out.println(Thread.currentThread().getName() + ":" + i);
		}
	}
}

sleep()和wait()的区别:

  • (1)sleep()来自Thread类,wait()来自Object类。
  • (2)sleep是Thread的静态类方法,谁调用的谁去睡觉;wait()是Object类的非静态方法
  • (3)sleep()释放资源不释放锁,wait()释放资源释放锁; 
  • (4)wait,notify和notifyAll只能在同步方法或者同步块里面使用,而sleep可以在任何地方使用

线程的状态

  • 新建:new一个Thread对象或者其子类对象就是创建一个线程,只是对象线程对象开辟了内存空间和初始化数据。            
  • 就绪:新建的对象调用start方法,就开启了线程,线程就到了就绪状态。 在这个状态的线程对象,具有执行资格,没有执行权。
  • 运行:当线程对象获取到了CPU的资源。      在这个状态的线程对象,既有执行资格,也有执行权。
  • 冻结:运行过程中的线程由于某些原因(比如wait,sleep),释放了执行资格和执行权。
  • 死亡:当线程对象调用的run方法结束,或者直接调用stop方法,就让线程对象死亡,在内存中变成了垃圾。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值