多线程的创建、线程的状态和调度and同步、join和yield以及单例设计模式的种类
创建线程方式 重要
继承Thead
实现Runnable接口,重写run方法
package com.qfedu.thread; /** * 实现Runnable接口,重写run()方法 * @author renrui * */ public class MyRunnable implements Runnable { @Override public void run() { // TODO Auto-generated method stub for(int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }
package com.qfedu.thread; public class App { public static void main(String[] args) { // TODO Auto-generated method stub // 使用线程,推荐通过实现Runnable接口方式 MyRunnable myRunnable = new MyRunnable(); // 创建线程 Thread t1 = new Thread(myRunnable); // 获取优先级 默认优先级5 System.out.println(t1.getPriority()); // 设置优先级 1-10 // t1.setPriority(10); t1.setPriority(Thread.MAX_PRIORITY); Thread t2 = new Thread(myRunnable); // 启动线程 t1.start(); t2.start(); // 使用匿名内部类 Thread t3 = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub for(int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }); t3.start(); } }
java中线程的状态 重要
6状态
1)新建状态new: 调用new 创建对象
2)可运行状态runnable: 调用start(),进入可运行状态,可以认为可运行状态中包含就绪和运行两个状态
3)阻塞状态blocking:某个线程调用同步方法,还没有释放锁时,其他线程也访问该方法,进行阻塞状态。当线程执行完毕释放锁,那些阻塞的线程进入可运行状态
4)等待状态waiting:调用wait()/join()等方法后,线程进入等待状态。当join对应的线程执行完毕,或者wait()的线程被唤醒,等待的线程进入可运行状态
5)计时等待状态time waiting: 和等待状态类型,调用sleep(long)/wait(long)/join(long)等方法,进入计时等待状态
6)终止状态terminated:run方法执行完毕,或者线程异常,进入终止状态
线程的调度
sleep 线程睡眠
某个线程中调用sleep方法,该线程睡眠,进入计时等待状态
package com.qfedu.sleep; public class App { public static void main(String[] args) { // TODO Auto-generated method stub MyThread t = new MyThread(); t.start(); try { // 受检异常 // 线程睡眠 参数表示睡眠时间 单位毫秒 // main方法中调用sleep,主线程休眠 Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } for(int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } } class MyThread extends Thread { @Override public void run() { try { Thread.sleep(20); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } for(int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }
join 了解
线程插入 A 线程中,调用B.join() B线程就会插队,B线程执行完,执行其他线程逻辑
package com.qfedu.join; public class App { public static void main(String[] args) { // TODO Auto-generated method stub MyThread t = new MyThread(); t.start(); for(int i = 0; i < 500; i++) { if(i == 20) { try { // 线程插队 // main方法中调用join方法的,main主线程进入等待状态 // t线程插队,插队的线程执行完毕,等待的线程才会继续执行 // t.join(); // 线程插队时,指定一个时间,单位毫秒 // 插队的线程执行完毕,或者插队时间到,等待的线程进入可运行状态 // main和t共同竞争cpu资源 t.join(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + ":" + i); } } } class MyThread extends Thread { @Override public void run() { for(int i = 0; i < 500; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }
yield 了解
线程礼让,某个线程中调用yield方法,该线程进入就绪状态
package com.qfedu.yield; public class App { public static void main(String[] args) { // TODO Auto-generated method stub Product p = new Product(); Customer c = new Customer(); p.start(); c.start(); } } class Product extends Thread { @Override public void run() { for(int i = 0; i < 100; i++) { if(i == 8) { // 调用yield方法之前,线程之间是竞争关系 // 线程礼让 // 调用礼让方法的线程进入就绪状态 // 和其他线程一起竞争cpu资源 Thread.yield(); } System.out.println(Thread.currentThread().getName() + ":" + i); } } } class Customer extends Thread { @Override public void run() { for(int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } }
线程同步 重要
主要为了解决线程安全的问题
多个线程访问线程共享的数据时 ,要注意线程安全问题
课件中,使用同步机制,实现线程同步
同步代码块
synchronized(锁对象) {}
同步方法
synchronized 返回值 方法名(参数) {}
注意:锁对象需要唯一,多个需要共享一把锁
package com.qfedu.syn; public class App { public static void main(String[] args) { // TODO Auto-generated method stub Ticket ticket = new Ticket(); // 启动了50个线程 for(int i = 0; i < 50; i++) { Thread t = new Thread(ticket); t.start(); } } } class Ticket implements Runnable { // 初始有100张票 // 50个线程共享num private int num = 100; @Override public void run() { // TODO Auto-generated method stub // num--; // System.out.println("剩余票数:" + num); // 调用同步方法 // A线程先抢到cpu资源,执行同步方法,加锁, // A线程执行代码的过程中,失去cpu的控制权(调度系统将cpu分配给其他线程),B线程抢到cpu,执行同步方法 // B执行同步方法时,A线程还没有执行完代码,锁依旧存在,B就会阻塞,等着A释放锁 // A释放锁后,其他阻塞的线程进入就绪状态,哪个线程先抢到cpu,哪个线程就可以执行同步方法,并加锁 // 注意:A执行完,释放锁,其他线程,比如BCDEF等线程,还需要抢占cpu资源,而不是B先执行 // 阻塞的线程会在“锁池”中记录(了解) // show(); // 同步代码块 // 基本语法:synchronized(代表锁的对象) {执行的逻辑} // 50个线程共用一个ticket对象,this表示当前的ticket对象,所以this可作为多个线程公用的锁 synchronized (this) { // synchronized (Ticket.class) { // 该写法了解一下 Ticket.class表示获取Ticket的Class对象 num--; System.out.println(Thread.currentThread().getName() + ",剩余票数:" + num); } } // 同步方法,在代码中“锁”也是对象,同步方法中,使用的锁是this对象 public synchronized void show() { num--; System.out.println(Thread.currentThread().getName() + ",剩余票数:" + num); } }
单例设计模式 次重要
某个类,在整个程序中只能有一个对象的时候,就可以使用单例模式
懒汉式
package com.qfedu.singleton; /** * 懒汉式 考虑多线程环境的线程安全问题 * @author renrui * */ public class Singleton { private static Singleton singleton = null; // 构造方法设置成私有的,类外不能通过new 创建对象 private Singleton() {} // 在类内部创建对象 public static synchronized Singleton getInstance() { if(singleton == null) { singleton = new Singleton(); return singleton; } else { return singleton; } } }
饿汉式
package com.qfedu.singleton; /** * 饿汉式 推荐 * @author renrui * */ public class Singleton2 { // 类加载的时候创建静态变量,只会执行一次 // 定义静态变量,直接创建对象并赋值 private static Singleton2 singleton2 = new Singleton2(); private Singleton2() {} public static Singleton2 getInstance() { return singleton2; } }