线程入门
1.创建线程的三种方式
1.1 继承Thread类,重写run()
public class ExtendThread extends Thread { //继承Thread类
@Override
public void run() { //重写run()方法, run()方法中包含线程的执行体
for (int i = 0; i < 1000; i++) {
System.out.println("画圆");
}
}
public static void main(String[] args) {
//创建实例,start()使线程进入就绪状态
ExtendThread extendThread = new ExtendThread();
extendThread.start();
//main线程,用于与创建的线程对比
for (int i = 0; i < 1000; i++) {
System.out.println("画方");
}
}
}
1.2 实现Runnable接口,重写run()
public class ImplementsRunnable implements Runnable {
//重写run(), run()方法中包含线程的执行体
@Override
public void run() {
//Thread.sleep()使线程休眠,模拟延时
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 20; i++) {
System.out.println("画圆");
}
}
public static void main(String[] args) {
//创建实现接口的实例
ImplementsRunnable implementsRunnable = new ImplementsRunnable();
//创建线程的语法
//Thread thread = new Thread(runnable);
//start()使线程进入就绪状态
//thread.start();
//简化为下
new Thread((implementsRunnable)).start();
//main线程,用于与创建的线程对比
for (int i = 0; i < 20; i++) {
System.out.println("画方块");
}
}
}
应用: 龟兔赛跑
//龟兔赛跑
public class Race implements Runnable {
static String Winner = null;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println(Thread.currentThread().getName() + " 跑了 " + i + " 步");
//跑到终点,产生胜者,结束循环
if (gameOver(i)) {
Winner = Thread.currentThread().getName();
System.out.println(Winner+" 赢了");
break;
}
//已存在胜者,结束循环
if (Winner != null) {
break;
}
}
}
private boolean gameOver(int step) {
if (step >= 100) {
return true;
} else {
return false;
}
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
1.3 实现Callable接口,重写call()
public class ImplementsCallable implements Callable<Integer> { //Callable后加上泛型
//该 call() 方法将作为线程执行体,并且有返回值。
@Override
public Integer call() throws Exception {
int i;
for (i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
return i;
}
public static void main(String[] args) {
//创建 Callable接口实现类的实例
ImplementsCallable ctt = new ImplementsCallable();
//使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值
//类似C++中容器
FutureTask<Integer> ft = new FutureTask<>(ctt);
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " 的循环变量i的值" + i);
if (i == 20) {
//使用 FutureTask类的对象作为 Thread 对象的 target 创建并启动新线程
new Thread(ft, "有返回值的线程").start();
}
}
//调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值
try {
System.out.println("子线程的返回值:" + ft.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.操作线程
sleep()
Thread.sleep(millis);
用sleep()模拟网络延时,放大问题的发生可能性
每个对象都有一把锁,sleep()不会释放锁,类似于抱着锁睡觉
//应用: 模拟倒计时
public class CountDown {
public static void main(String[] args) {
count(10);
}
static void count(int time) {
for (; time >= 0; time--) {
try {
Thread.sleep(1000); // 模拟延时, 1000ms
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(time + " second left...");
}
}
}
stop()
在Runnable的实现类中定义标志位, 并定义一个用于转换标志位停止线程的stop()
方法, 在线程体run()中通过判断标志位状态来控制线程
public class ThreadStop implements Runnable {
//定义标志位
boolean flag = true;
//线程体
@Override
public void run() {
//线程体使用标志位控制
while (flag) {
//循环输出,直至主线程中for循环到第80次
System.out.println("Thread is running...");
}
}
//定义一个公开的stop()方法,用于转换标志位,停止线程
public void stop() {
this.flag = false;
System.out.println("分线程停止了");
}
public static void main(String[] args) {
ThreadStop runnable = new ThreadStop();
Thread thread = new Thread(runnable);
thread.start();
//主线程
for (int i = 0; i < 100; i++) {
System.out.println("main is running..." +i);
if (i == 80) {
//stop方法,转换标志位
runnable.stop();
}
}
}
}
getState()
线程的六种基本状态:
新建状态(New) 即用new关键字新建一个线程,这个线程就处于新建状态。
就绪状态(Runnable) 操作系统中的就绪和运行两种状态,在Java中统称为RUNNABLE。
等待状态(WAITING) 进入该状态表示当前线程需要等待其他线程做出一些的特定的动作(通知或中断)。
超时等待状态(TIMED_WAITING) ** 区别于WAITING,它可以在指定的时间**自行返回。
阻塞状态(Blocked) 阻塞状态表示线程正等待监视器锁,而陷入的状态。
消亡状态(Dead) 即线程的终止,表示线程已经执行完毕。
使用getState()
方法能够获得线程当前所处的状态
public class ThreadState {
public static void main(String[] args) throws InterruptedException {
//使用lambda表达式,创建Thread实例,重写run()方法
Thread thread = new Thread(() -> {
for (int i = 0; i < 50; i++) {
if (i % 5 == 0) {
try {
Thread.sleep(100); //3.TIMED_WAITING
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("---------------------");
//4.TERMINATED
});
System.out.println("thread.getState() = " + thread.getState()); //1.NEW
thread.start(); //2.RUNNABLE
while (thread.getState() != Thread.State.TERMINATED) {
System.out.println("thread.getState() = " + thread.getState());
Thread.sleep(100);
}
}
}
setPriority()
使用setPriority()
方法来设置线程的优先级
setPriority()
方法的参数为可以为Thread的枚举类型属性, 或是1~10的整数, 数字越大优先级越高
public class ThreadPriority {
public static void main(String[] args) {
//主线程默认优先级
System.out.println(Thread.currentThread().getName()+" --> "+ Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority, "t1");
Thread t2 = new Thread(myPriority, "t2");
Thread t3 = new Thread(myPriority, "t3");
Thread t4 = new Thread(myPriority, "t4");
//先设置优先级再启动
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);
t4.start();
}
}
class MyPriority implements Runnable {
@Override
public void run() {
//执行体打印当前线程名和优先级
System.out.println(Thread.currentThread().getName()+" --> "+ Thread.currentThread().getPriority());
}
}
join()
join()
调用合并线程,其他线程阻塞, 待调用join的线程执行完毕后,再执行其他线程,类似插队
public class ThreadJoin implements Runnable {
public static void main(String[] args) throws InterruptedException {
ThreadJoin threadJoin = new ThreadJoin();
Thread thread = new Thread(threadJoin, "分线程");
thread.start();
for (int i = 0; i < 200; i++) {
//main线程的i=100时,分线程插入,main线程阻塞
if (i == 100) thread.join();
//分线程完全结束后,main线程继续执行
System.out.println("main is running..." + i);
}
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + " is running..." + i);
}
}
}
yield()
yield()
使得线程礼让,让运行状态的线程重新进入就绪状态, 等待CPU的调度
public class ThreadYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"Apple").start();
new Thread(myYield,"Google").start();
}
}
class MyYield implements Runnable {
@Override
//首次运行时从头开始执行run()
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行。。。");
//线程礼让, 暂停线程, 重新进入就绪状态等待调度
Thread.yield();
//礼让后继续执行yield()语句后的代码
System.out.println(Thread.currentThread().getName()+"线程停止执行。。。");
}
}
setDaemon()
线程分为用户线程(如main…)和守护线程(如垃圾回收机制gc…)
虚拟机不必等待守护线程结束才结束进程
public class ThreadDaemon {
public static void main(String[] args) {
God god = new God();
People people = new People();
Thread thread_god = new Thread(god);
thread_god.setDaemon(true);//通过setDaemon()方法将thread_god线程设为守护线程
Thread thread_people = new Thread(people);
thread_god.start();
thread_people.start();
}
}
class God implements Runnable {
@Override
public void run() {
//发现死循环并不会一直执行,到用户线程结束即结束
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("god bless you!");
}
}
}
class People implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("I am happy!");
}
System.out.println("Goodbye World!");
}
}
3.线程池
创建线程池使用Executors.newFixedThreadPool(nThread)
静态工厂方法,参数为池子大小
public class ThreadPool {
public static void main(String[] args) {
//1.创建服务,创建线程池
// Executors.newFixedThreadPool() 静态工厂方法,参数为池子大小
ExecutorService service = Executors.newFixedThreadPool(10);
//2.将线程放入线程池
service.execute(new MyRunnable());
service.execute(new MyRunnable());
service.execute(new MyRunnable());
service.execute(new MyRunnable());
//3.关闭线程池
service.shutdown();
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running...");
}
}
4.锁
在并发编程中存在线程安全问题,主要原因有:1.存在共享数据 2.多线程共同操作共享数据。
当多线程同时操作同一个对象, 会导致数据紊乱, 线程不安全, 故引入锁
为了解决这些问题, 引入了Synchronized
和ReentrantLock
两种方式
Synchronized
语法为: synchronized (需要上锁的对象) { 代码块 }
public class BuyTicket implements Runnable {
//票数
private Integer ticketNum = 20;
@Override
public void run() {
//循环抢票
while (true) {
//引入锁
//使用synchronized锁上ticketNum,使得多个线程不能同时对ticketNum进行操作
//执行完一次代码块中的内容就释放锁
synchronized (ticketNum) {
if (ticketNum <= 0) {
break;
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + (ticketNum--) + "张票");
}
}
}
public static void main(String[] args) {
//创建Runnable对象
BuyTicket buyTicket = new BuyTicket();
//三个线程操作一个Runnable对象
new Thread(buyTicket, "小红").start();
new Thread(buyTicket, "小黄").start();
new Thread(buyTicket, "小蓝").start();
}
}
ReentrantLock
public class LockDemo {
public static void main(String[] args) {
//创建Runnable对象
Ticket ticket = new Ticket();
//三个线程操作一个Runnable对象
new Thread(ticket, "小红").start();
new Thread(ticket, "小黄").start();
new Thread(ticket, "小蓝").start();
}
}
class Ticket implements Runnable {
private int number = 1000;
//定义ReentrantLock类的Lock锁
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
//lock()加锁
lock.lock();
//加锁后判断,不能让多个线程同时判断
if (number <= 0) {
System.out.println(Thread.currentThread().getName() + "--> 没票了");
break;
}
System.out.println(Thread.currentThread().getName() + " 拿到了 " + (number--) + " 号票");
//unlock()解锁
lock.unlock();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
5.死锁
多个线程互相持有对方所需要的资源,相互等待,形成僵持
public class DeadLock {
public static void main(String[] args) {
Thread lea = new Thread(new Girl("Lea", 1));
Thread anna = new Thread(new Girl("Anna", 0));
lea.start();
anna.start();
}
}
//镜子类
class Mirror {
}
//口红类
class LipStick {
}
class Girl implements Runnable {
private String name;
private int choice;
//mirror,lipStick设为static,表示只有一个镜子和口红
static Mirror mirror = new Mirror();
static LipStick lipStick = new LipStick();
//构造器 choice=0或1
public Girl(String name, int choice) {
this.name = name;
this.choice = choice;
}
@Override
public void run() {
makeup();
}
void makeup() {
//choice为0,先获得镜子的锁,再等待拿口红的锁
if (choice == 0) {
synchronized (mirror) {//获得镜子锁
System.out.println(this.name + " 获得了镜子的锁");
try {
Thread.sleep(100);//模拟延时,保证另一个人拿到了另一件物品的锁
} catch (InterruptedException e) {
e.printStackTrace();
}
//锁中拿锁,造成死锁
synchronized (lipStick) {//获得口红锁
System.out.println(this.name + " 获得了口红的锁");
}
}
} else {
synchronized (lipStick) {//获得口红锁
System.out.println(this.name + " 获得了口红的锁");
try {
Thread.sleep(200);//模拟延时,保证另一个人拿到了另一件物品的锁
} catch (InterruptedException e) {
e.printStackTrace();
}
//锁中拿锁,造成死锁
synchronized (mirror) {//获得镜子锁
System.out.println(this.name + " 获得了镜子的锁");
}
}
}
}
}