多线程–k7
真正的多线程 — 需要多核CPU支持
单核Cup通过切换实现的看起来像是多线程的结果(了解)
ArrayList本身是线性不安全的 可以通过 Collections.synchronized(list) 方法吧list转换成线性安全的
创建和使用线程
继承Thread类、 实现Runnable接口、实现Callable接口、线程池
1.如何自定义线程类
1)继承Thread类
定义三个类继承Thread类
/**
* 创建线程的方式,继承Thread类
* 1.继承Thread类
* 2.重写Thread类中的run方法
*/
public class Chat extends Thread{
//重写Thread类中的run方法---线程做的所有的工作都在run方法中
@Override
public void run(){
for (int i = 0; i < 10 ; i++) {
System.out.println("聊天...");
//延时
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Drink extends Thread{
//重写Thread类中的run方法---线程做的所有的工作都在run方法中
@Override
public void run(){
for (int i = 0; i < 10 ; i++) {
System.out.println("喝点小酒...");
//延时
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Eat extends Thread{
//重写Thread类中的run方法---线程做的所有的工作都在run方法中
@Override
public void run(){
for (int i = 0; i < 10 ; i++) {
System.out.println("吃个饭...");
//延时
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* java多线程编程 --- 本质 自定义线程类并创建自定义线程类的对象
* 1.如何自定义线程类
* 1)继承Thread类
* 2)实现Runnable接口
*/
public class MyTest1 {
public static void main(String[] args) {
//创建自定义线程类的对象
Chat chat = new Chat();
Drink drink = new Drink();
Eat eat = new Eat();
//启动线程 --- 线程的start方法,一定不要直接调用run方法
chat.start();
drink.start();
eat.start();
}
}
2)实现Runnable接口 (避免了单继承的局限性 适合多线程共享同一份资源)
还是那三个类 这次让他们实现Runnable接口
public class ChatThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("聊天...");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
另外两个类同上仿写
public class MyTest {
public static void main(String[] args) {
ChatThread chatThread = new ChatThread();
DrinkThread drinkThread = new DrinkThread();
EatThread eatThread = new EatThread();
//创建线程
Thread chat = new Thread(chatThread);
Thread drink= new Thread(drinkThread);
Thread eat = new Thread(eatThread);
//匿名内部类
Thread sing = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("唱歌...");
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
//创建线程
chat.start();
drink.start();
eat.start();
sing.start();
}
}
2.Thread 常用的方法
先定义一个类继承Tread类
public class MyThread extends Thread{
@Override
public void run(){
//getName() 获取线程的名字
System.out.println(Thread.currentThread().getName() + "************************************************");
}
}
现在堆Thread常用的方法进行测试
public class MyTest1 {
public static void main(String[] args) {
/**
* Thread方法
* 1.staart() 启动线程,不能直接调用run方法
* 2.run() 线程在被调用时执行的操作
* 3.getName() 获取当前线程的名称
* 4.setName() 设置线程的名称,如果没有设置,线程存在默认的名字
* 5.currentThread() 返回当前的线程
*
* main方法自身也是一个线程
*/
//设置main线程的名字
Thread.currentThread().setName("主线程");
//创建线程对象
MyThread myThread = new MyThread();
//设置线程的名字
myThread.setName("线程A");
//启动线程
myThread.start();
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " ++++++++++++++++++++++++++++++++++++++++++++++ ");
}
}
}
3.线程的调度
public class MyTest2 {
public static void main(String[] args) throws InterruptedException {
/**
* 线程的调度
* 策略:
* 1.基于时间片轮转 时间片---非常非常短的时间间隔 轮转--轮流
* 2.抢占式:高优先级的线程抢占CPU 概率 优先级高的线程运行的概率大
*/
//获取线程的优先级 --- 默认是5
System.out.println(Thread.currentThread().getName()+ "优先级" + Thread.currentThread().getPriority());
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);//1
MyThread myThread = new MyThread();
//设置线程的优先级
myThread.setPriority(Thread.MAX_PRIORITY);//10
System.out.println(myThread.getName()+"优先级"+myThread.getPriority());
myThread.start();
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "++++++++++++++++++++++++++++++++");
if(i == 2){
myThread.join();
}
}
}
4.线程同步
线程安全问题:多线程共享一份资源就会出现线程安全问题
线程同步的三种方式:
1.synchronized代码块
2.synchronized方法
3.Lock锁(了解)
线性安全的单例设计模式–懒汉式
//synchronized代码块方式
public class SingleObject {
private static SingleObject obj = null;
private SingleObject() {
}
public static SingleObject getInstance() {
synchronized(SingleObject.class) {
if(obj == null) {
obj = new SingleObject();
}
}
return obj;
}
}
//synchronized方法方式
public class SingleObject {
private static SingleObject obj = null;
private SingleObject() {
}
public static synchronized SingleObject getInstance() {
if(obj == null) {
obj = new SingleObject();
}
return obj;
}
}
//Lock方式
public class SingleObject {
private static SingleObject obj = null;
private ReentrantLock lock = new ReentrantLock(true);
private SingleObject() {
}
public static SingleObject getInstance() {
try {
lock.lock();
if(obj == null) {
obj = new SingleObject();
}
} finally {
lock.unlock();
}
return obj;
}
}
5.线程通信
wait()
:令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当前线程排队等候其他线程调用notify()
或notifyAll()
方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行;notify()
:唤醒正在排队等待同步资源的线程中优先级最高者结束等待;notifyAll()
:唤醒正在排队等待资源的所有线程结束等待。
以上三个方法只有在synchronized
方法或synchronized
代码块中才能使用。
package com.sdut.k7.text1;
/**
* 线程通信
*/
public class Window implements Runnable{
private int count = 1;//1~100
/**
* wait() 令当前线程挂起并放弃CPU、同步资源并等待
*
* notify() 唤醒正在排队等待同步资源的线程中优先级最高者结束等待;
*/
@Override
public void run() {
while (true) {
//synchronized 代码块
synchronized (Object.class) {
Object.class.notify();
if (count <= 100) {
System.out.println(Thread.currentThread().getName() + "卖出编号为" + count + "的票");
count++;
} else {
break;
}
try {
Object.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 模拟三个窗口售票 --- 三个窗口销售同一份票 100张
* 1)100张票被三个窗口共享
* 2)如何创建三个窗口 --- 三个线程
* 实现Runnable接口
*
* 线程同步
*/
public class MyTest1 {
public static void main(String[] args) {
Window window = new Window();
Thread w1 = new Thread(window);
Thread w2 = new Thread(window);
Thread w3 = new Thread(window);
w1.start();
w2.start();
w3.start();
}
}
总结:
多线程
程序:静
进程;动
线程:main
main JavaSe进程
创建线程的方式
1)继承Thread类
*自定义线程类,继承Thread类
*重写Thread类中的run方法 run()—线程所有的操作都写在run()方法中
*根据自定义线程类创建线程对象
*start()
2)实现Rannable接口
*自定义线程类,实现Rannable接口
*重写Rannable接口中的run方法 run()—线程所有的操作都写在run()方法中
*根据自定义线程类创建线程对象
*创建Thread类对象,将上一步的对象传递到构造方法中
*start()
Thread常用的方法
线程的调度
线程同步
线程通信
** 生产者与消费者案例 **
引入:生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个线程——即所谓的生产者
和消费者
——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。
创建线程的第三种方式
实现Callable接口
与使用Runnable
相比, Callable
功能更强大些
-
相比
run()
方法,可以有返回值; -
方法可以抛出异常;
-
支持泛型的返回值;
-
需要借助
FutureTask
类,比如获取返回结果。import java.util.concurrent.Callable; /** * 使用实现Callable接口的方式创建线程 */ public class NumThread implements Callable<Integer> { //重写call方法 @Override public Integer call() throws Exception { //1+2+3+4+...+100 int sum = 0; for(int i = 1; i <= 100; i++) { sum += i; } return sum; } } public class MyTest8 { public static void main(String[] args) { NumThread numThread = new NumThread(); FutureTask<Integer> task = new FutureTask<>(numThread); //创建线程 Thread thread = new Thread(task); //启动线程 thread.start(); //设置线程优先级 thread.setPriority(Thread.MAX_PRIORITY); try { Integer sum = task.get(); System.out.println(sum); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
创建线程的第四种方式–线程池
1.池化技术
|— 线程池
|— 连接池
|— 内存池
2.为什么要使用池化技术
在系统刚启动还没创建很多线程的时候就先把线程创建好放到池子里 到时候访问就不用创建大量线程 直接访问就行 大大提高性能
public class NumberThread implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
if(i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class NumberThread1 implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
if(i % 2 != 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class MyTest9{
public static void main(String[] args) {
//创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
NumberThread numberThread = new NumberThread();
NumberThread1 numberThread1 = new NumberThread1();
//执行线程
service.execute(numberThread);
service.execute(numberThread1);
//关闭线程
service.shutdown();
}
}
00; i++) {
if(i % 2 != 0) {
System.out.println(Thread.currentThread().getName() + “:” + i);
}
}
}
}
public class MyTest9{
public static void main(String[] args) {
//创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
NumberThread numberThread = new NumberThread();
NumberThread1 numberThread1 = new NumberThread1();
//执行线程
service.execute(numberThread);
service.execute(numberThread1);
//关闭线程
service.shutdown();
}
}