1.基本概念
高并发和高并行 高并行:同一时刻,多个指令在多个cpu同时执行 高并发 :同一时刻,多个指令在单个cpu上交替执行
进程和线程 进程:正在运行的程序 线程:进程中单个的顺序控制流,是一条执行路径
主线程和其他线程 主线程:进程中只有一条线程的,这个线程叫主线程 其他线程:进程中除了主线程的线程
2.实现多线程的方式
2.1创建类继承Thread类,重写run方法
public class AThread extends Thread{ @Override public void run() { System.out.println("AThread线程开启了"+Thread.currentThread().getName()); } }
publicclassDemo{
publicstaticvoidmain(String[] args){
AThread at = new AThread();
at.start();
AThread at1 = new AThread();
at1.start();
new AThread().start();
}
}
2.2创建接口实现Runnable接口,实现run方法
public class ARunable implements Runnable { @Override public void run() { System.out.println("ARunnable线程开启了"+Thread.currentThread().getName()); } }
publicclassDemo{
publicstaticvoidmain(String[] args){
new Thread(new ARunable()).start();
new Thread(new ARunable()).start();
new Thread(() -> System.out.println("线程开启了"+Thread.currentThread().getName())).start();
}
}
2.3创建接口实现Callable接口,实现call方法(优先级最低)
public class ACallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("线程开启了"+Thread.currentThread().getName());
return "线程结束了";
}
}
publicclassDemo{
publicstaticvoidmain(String[] args)throws ExecutionException, InterruptedException {
ACallable ac = new ACallable();
FutureTask<String > ft = new FutureTask<>(ac);
Thread tr =new Thread(ft);
tr.start();
String s = ft.get();
System.out.println(s);
}
}
3.线程相关api
//1.设置线程的名称(2个)(setName())publicclassThreadGet{
publicstaticvoidmain(String[] args){
Thread t =new Thread();
t.setName("线程1");
}
}
//2.获取线程名称(getName())publicclassThreadGet{
publicstaticvoidmain(String[] args){
Thread t =new Thread();
t.getName();
}
}
//3.获取当前的线程(Thread.currentThread().getName())publicclassARunableimplementsRunnable{
@Overridepublicvoidrun(){
System.out.println(Thread.currentThread().getName());
}
}
//4.线程休眠(单位是毫秒)publicclassAThreadextendsThread{
@Overridepublicvoidrun(){
System.out.println("线程启动了");
}
}
publicclassDemo{
publicstaticvoidmain(String[] args){
AThread at = new AThread();
at.start();
AThread at1 = new AThread();
at1.start();
new AThread().start();
}
}
//5.设置当前线程的优先级(1-10),默认是5,10的优先级最高//6.设置线程为守护线程(最后一个非守护线程结束后,守护线程也会结束)
4.线程安全问题
结论:多线程同时操作类中的全局成员变量时一定会导致线程安全问题
4.1多窗口卖票
问题代码
publicclassTicketimplementsRunnable{
//票的数量/* 问题代码
* ticket全局成员变量可能被多个线程同时访问,所以一定会由线程安全问题
*/privateint ticket = 100;
privatelong startTime;
@Overridepublicvoidrun(){
while (true) {
if (ticket <= 0) {
//卖完了long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime);
break;
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
}
}
}
}
重复票,负数票出现原因
重复票:多个线程依次执行完负数票:多个线程同时进入执行
5.如何解决线程安全问题
5.1同步代码块
public class Ticket extends Thread {
privateint ticket = 100;
@Override
public void run() {
while (true) {
synchronized (String.class) {
if (ticket<=0){
break;
}else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName()+"再卖票,还剩下"+ticket+"张票");
}
}
}
}
}
public class Demo {
public static void main(String[] args) {
Ticket t = newTicket();
Thread t1 = newThread(t);
Thread t2 = newThread(t);
Thread t3 = newThread(t);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
5.2同步方法
publicclassTicketDemo3extendsThread{
privateint ticket = 100;
@Overridepublicvoidrun(){
while (true) {
boolean b = synchronizedMethod();
if (b) {
break;
}
}
}
publicsynchronizedbooleansynchronizedMethod(){
if (ticket == 0) {
returntrue;
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName() + "再卖票,还剩下" + ticket + "张票");
returnfalse;
}
}
}
5.3同步代码块和同步方法的所各自特点
1.同步代码可以锁住指定代码,同步方法可以锁住方法里面的所有代码2.同步代码块可以指定所锁对象,同步方法不能指定所对象
5.4自己创建锁来进行管理(ReentrantLock)
publicclassTcketDemo4extendsThread{
privateint ticket = 100;
private ReentrantLock rt = new ReentrantLock();
@Overridepublicvoidrun(){
while (true) {
rt.lock();
if (ticket <= 0) {
break;
} else {
try {
Thread.sleep(100);
ticket--;
System.out.println(Thread.currentThread().getName() + "再卖票,还剩下" + ticket + "张票");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rt.unlock();
}
}
}
}
}
publicclassDemo4{
publicstaticvoidmain(String[] args){
TcketDemo4 t = new TcketDemo4();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.start();
t2.start();
t3.start();
}
}
6.阻塞队列
6.1生产者消费者模型(理解)
多线程01
1.基本概念
高并发和高并行 高并行:同一时刻,多个指令在多个cpu同时执行 高并发:同一时刻,多个指令在单个cpu上交替执行
进程和线程 进程:正在运行的程序 线程:进程中单个的顺序控制流,是一条执行路径
主线程和其他线程 主线程:进程中只有一条线程的,这个线程叫主线程 其他线程:进程中除了主线程的线程
2.实现多线程的方式
2.1创建类继承Thread类,重写run方法
public class AThread extends Thread{ @Override public void run() { System.out.println("AThread线程开启了"+Thread.currentThread().getName()); } }
public class Demo {
public static void main(String[] args) {
AThread at = new AThread();
at.start();
AThread at1 = new AThread();
at1.start();
new AThread().start();
}
}
2.2创建接口实现Runnable接口,实现run方法
public class ARunable implements Runnable { @Override public void run() { System.out.println("ARunnable线程开启了"+Thread.currentThread().getName()); } }
public class Demo {
public static void main(String[] args) {
new Thread(new ARunable()).start();
new Thread(new ARunable()).start();
new Thread(() -> System.out.println("线程开启了"+Thread.currentThread().getName())).start();
}
}
2.3创建接口实现Callable接口,实现call方法(优先级最低)
public class ACallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("线程开启了"+Thread.currentThread().getName());
return "线程结束了";
}
}
public class Demo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ACallable ac = new ACallable();
FutureTask<String > ft = new FutureTask<>(ac);
Thread tr =new Thread(ft);
tr.start();
String s = ft.get();
System.out.println(s);
}
}
3.线程相关api
//1.设置线程的名称(2个)(setName())
public class ThreadGet {
public static void main(String[] args) {
Thread t =new Thread();
t.setName("线程1");
}
}
//2.获取线程名称(getName())
public class ThreadGet {
public static void main(String[] args) {
Thread t =new Thread();
t.getName();
}
}
//3.获取当前的线程(Thread.currentThread().getName())
public class ARunable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
//4.线程休眠(单位是毫秒)
public class AThread extends Thread{
@Override
public void run() {
System.out.println("线程启动了");
}
}
public class Demo {
public static void main(String[] args) {
AThread at = new AThread();
at.start();
AThread at1 = new AThread();
at1.start();
new AThread().start();
}
}
//5.设置当前线程的优先级(1-10),默认是5,10的优先级最高
//6.设置线程为守护线程(最后一个非守护线程结束后,守护线程也会结束)
4.线程安全问题
结论:多线程同时操作类中的全局成员变量时一定会导致线程安全问题
4.1多窗口卖票
问题代码
public class Ticket implements Runnable {
//票的数量
/*问题代码
*ticket全局成员变量可能被多个线程同时访问,所以一定会由线程安全问题
*/
private int ticket = 100;
private long startTime;
@Override
public void run() {
while (true) {
if (ticket <= 0) {
//卖完了
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime);
break;
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
}
}
}
}
重复票,负数票出现原因
重复票:多个线程依次执行完
负数票:多个线程同时进入执行
5.如何解决线程安全问题
5.1同步代码块
public class Ticket extends Thread {
private int ticket = 100;
@Override
public void run() {
while (true) {
synchronized (String.class) {
if (ticket<=0){
break;
}else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName()+"再卖票,还剩下"+ticket+"张票");
}
}
}
}
}
public class Demo {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
5.2同步方法
public class TicketDemo3 extends Thread {
private int ticket = 100;
@Override
public void run() {
while (true) {
boolean b = synchronizedMethod();
if (b) {
break;
}
}
}
public synchronized boolean synchronizedMethod() {
if (ticket == 0) {
return true;
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName() + "再卖票,还剩下" + ticket + "张票");
return false;
}
}
}
5.3同步代码块和同步方法的所各自特点
1.同步代码可以锁住指定代码,同步方法可以锁住方法里面的所有代码
2.同步代码块可以指定所锁对象,同步方法不能指定所对象
5.4自己创建锁来进行管理(ReentrantLock)
public class TcketDemo4 extends Thread {
private int ticket = 100;
private ReentrantLock rt = new ReentrantLock();
@Override
public void run() {
while (true) {
rt.lock();
if (ticket <= 0) {
break;
} else {
try {
Thread.sleep(100);
ticket--;
System.out.println(Thread.currentThread().getName() + "再卖票,还剩下" + ticket + "张票");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
rt.unlock();
}
}
}
}
}
public class Demo4 {
public static void main(String[] args) {
TcketDemo4 t = new TcketDemo4();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.start();
t2.start();
t3.start();
}
}
6.阻塞队列
6.1生产者消费者模型(理解)
生产者消费者模型
6.2线程的等待唤醒机制
线程的等待和唤醒必须借助于同一把锁
锁对象.class.wait() 让当前的线程等着
锁对象.class.notify() 让锁对象的等着的线程苏醒
但是必须是同一个锁对象。
6.3阻塞队列的使用
阻塞队列
常见BlockingQueue
ArrayBlockingQueue: 底层是数组,有界
LinkedBlockingQueue: 底层是链表,无界.但不是真正的无界,最大为int的最大值
阻塞队列使用步骤
初始化
//阻塞队列初始化
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
生产者添加数据到队列
queue.put("汉堡包");
消费者从队列中获取数据
注意,取不到数据就会挂在哪里等,什么时候取到什么时候执行
String data = queue.take()
6.2线程的等待唤醒机制
线程的等待和唤醒必须借助于同一把锁
锁对象.class.wait() 让当前的线程等着锁对象.class.notify() 让锁对象的等着的线程苏醒但是必须是同一个锁对象。
6.3阻塞队列的使用
阻塞队列
常见BlockingQueue
ArrayBlockingQueue: 底层是数组,有界
LinkedBlockingQueue: 底层是链表,无界.但不是真正的无界,最大为int的最大值
阻塞队列使用步骤
初始化
//阻塞队列初始化
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(10);
生产者添加数据到队列
queue.put("汉堡包");
消费者从队列中获取数据
注意,取不到数据就会挂在哪里等,什么时候取到什么时候执行
String data = queue.take()