目录
线程的七大状态
一、多线程的创建
并发:同一时刻,多个指令在单个cpu交替执行
并行:同一时刻,多个指令在多个cpu同时执行
1.1、继承Thread的方式
public static void main(String[] args) {
/*
多线程第一种启动方式
1.自己定义一个类继承Thread
2.重写run方法
3.创建子类对象,启动线程
*/
MyThread t1 =new MyThread();
MyThread t2 =new MyThread();
t1.setName("线程1");
t1.setName("线程2");
t1.start();
t2.start();
}
public class MyThread extends Thread{
@Override
public void run() {
//书写线程要执行代码
for (int i = 0 ; i < 100 ; i++){
System.out.println(getName()+"abc");
}
}
}
1.2、Runnable接口方式
public class ThreadDemo {
public static void main(String[] args) {
多线程第二种启动方式
1.自己定义一个类实现Runnable接口
2.重写run方法
3.创建自己的类对象
4.创建一个Thread类的对象,并开启线程
*/
//创建MyRun的对象
//表示多线程要执行的任务
MyRun mr = new MyRun();
Thread t3 =new Thread(mr);
Thread t4 =new Thread(mr);
t3.setName("线程3");
t4.setName("线程4");
t3.start();
t4.start();
}
}
public class MyRun implements Runnable {
@Override
//书写程序要执行的代码
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "Hello");
//Thread.currentThread()返回当前线程的对象
}
}
}
1.3、实现Callable接口
package Threads;
import Threads.MyRun;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class ThreadDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
/*
多线程的第三种实现方式
特点:可以获取多线程运行的结果
1.创建一个MyCallable实现Callable接口
2.重写Call(该方法具有返回值,表示多线程运行的结果)
3.创建MyCallable的对象(表示多线程要执行的任务)
4.创建FutureTask的对象(作用管理多线程运行的结果)
5.创建Thread类的对象,并启动(表示线程)
*/
MyCallable mc = new MyCallable();
FutureTask<Integer> ft =new FutureTask<>(mc);
Thread t5 = new Thread(ft);
t5.start();
Integer result = ft.get();
System.out.println(result);
}
}
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
//求1~100的和
int sum =0;
for (int i = 1; i <=100; i++) {
sum = sum + i;
}
return sum;
}
}
二、多线程中的常用成员方法
三、多线程中的线程安全问题
3.1sychronized(锁)解决线程安全问题:
需求:三个窗口售卖100张电影票
主要问题:
1.多个窗口售卖同一张票
2.售卖票数超过规定票数
问题形成主要原因:
线程执行时,具有随机性,CPU的执行权随时可能发生抢占。
解决办法:
同步代码块/同步方法
使用
sychronized(锁){
操作共享数据的代码
}
特点1:锁默认打开,有一个线程进去了,锁自动关闭。
特点2:里面代码全部执行完毕,线程出来,锁自动打开。
同步代码块代码实现:
ThreadDemo.java文件
package Thread_safe;
public class ThreadDemo {
public static void main(String[] args) {
//创建线程对象
Mythread t1 =new Mythread();
Mythread t2 =new Mythread();
Mythread t3 =new Mythread();
//给线程对象设置名字
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
//开启线程
t1.start();
t2.start();
t3.start();
}
}
MyThread.java
package Thread_safe;
public class Mythread extends Thread{
//static修饰表示这个类的所有对象,都共享ticket的数据
static int ticket = 0 ;//0~99
//创建锁对象,一定要是唯一的
static Object obj = new Object();
@Override
public void run() {
while (true){
//同步代码块
synchronized (obj){
if (ticket<100) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
ticket++;
System.out.println(getName() + "正在售卖第" + ticket + "张票");
}
else {
break;
}
}
}
}
}
同步方法代码实现:
ThreadDemo.java文件
package Thread_safe;
public class ThreadDemo {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
Thread t3 = new Thread(mr);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
MyRunnable.java文件
package Thread_safe;
public class MyRunnable implements Runnable{
int ticket = 0;
public void run() {
while (true){
//同步方法
if (method()) break;
}
}
private synchronized boolean method() {
if (ticket<100) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
ticket++;
System.out.println(Thread.currentThread().getName() + "正在售卖第" + ticket + "张票");
}
else {
return true;
}
return false;
}
}
3.2使用Lock锁解决线程安全问题
对比synchronizedk()方法和语句可以获得更广泛的锁定操作
Lock中提供了获得锁和释放锁的方法
void lock() 获得锁
void unlock() 释放锁
Lock()是接口,不能直接实例化,采用他的实现类ReentrantLock来实例化
ReentrantLock的构造方法:
ReentrantLock():创建一个ReentrantLock的实例。
示例代码:
MyRunnable.java
package Thread_safe;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyRunnable implements Runnable{
int ticket = 0;
Lock lock =new ReentrantLock();
public void run() {
while (true){
//同步方法
if (method()) break;
}
}
private boolean method() {
lock.lock();
if (ticket<100) {
try {
Thread.sleep(100);
ticket++;
System.out.println(Thread.currentThread().getName() + "正在售卖第" + ticket + "张票");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
lock.unlock();//将unlock放入finally中避免进程跳出循环时直接跳过unlock导致程序无法结束
}
}
else {
return true;
}
return false;
}
}
ThreadDemo.java
package Thread_safe;
public class ThreadDemo {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
Thread t3 = new Thread(mr);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
四、多线程中的等待唤醒机制
4.1常规方式实现
原理图解:
基本方法:
实例代码:
Cook.java文件
package wait_and_notify;
import sun.security.krb5.internal.crypto.Des;
public class Cook extends Thread{
@Override
public void run() {
while (true){
synchronized (Desk.lock){
if (Desk.count==0){
break;
}else {
if (Desk.foolFlag==1){
try {
Desk.lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else {
System.out.println("做好了一碗面条");
Desk.foolFlag=1;
Desk.lock.notifyAll();
}
}
}
}
}
}
Desk.java文件:
package wait_and_notify;
public class Desk {
//0表示没有面条,1表示有面条。
public static int foolFlag = 0;
//锁对象创建
public static Object lock =new Object();
//总数
public static int count = 10;
}
Foodie.java文件
package wait_and_notify;
public class Foodie extends Thread {
@Override
public void run() {
while (true){
synchronized (Desk.lock){
if (Desk.foolFlag==0){
try {
Desk.lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else {
Desk.count--;
System.out.println("正在吃,还能吃:"+Desk.count+"碗面条");
Desk.lock.notifyAll();
Desk.foolFlag=0;
}
}
}
}
}
ThreadDemo.java文件
package wait_and_notify;
public class ThreadDemo {
public static void main(String[] args) {
Foodie f1 =new Foodie();
Cook c1 =new Cook();
f1.start();
c1.start();
}
}
4.2阻塞队列的方式实现
Cook.java文件
package wait_and_notify_ArrayBlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;
public class Cook extends Thread{
ArrayBlockingQueue<String> queue;
public Cook(ArrayBlockingQueue<String>queue){
this.queue=queue;
}
@Override
public void run() {
while (true){
try {
queue.put("面条");
System.out.println("放了一碗面条");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
Foodie
package wait_and_notify_ArrayBlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;
public class Foodie extends Thread{
ArrayBlockingQueue<String> queue;
public Foodie(ArrayBlockingQueue<String>queue){
this.queue=queue;
}
@Override
public void run() {
while (true){
try {
String food = queue.take();
System.out.println(food);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
ThreadDemo.java
package wait_and_notify_ArrayBlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;
public class ThreadDemo {
public static void main(String[] args) {
ArrayBlockingQueue<String> queue =new ArrayBlockingQueue<>(1);
Cook c = new Cook(queue);
Foodie f = new Foodie(queue);
c.start();
f.start();
}
}