Java多线程
多线程
线程:一个单一的顺序执行流程就是一个线程
多线程:多个顺序执行流程"同时"执行
创建方式
继承Thread重写run()方法
/*
* 多线程:
* 线程:一个单一的顺序执行流程就是一个线程
* 多线程:多个顺序执行流程"同时"执行
* 线程的第一种创建方式:
* 继承Thread
* 重写run()方法
* 调用线程的start()方法将线程启动
* 第一种方法优点:
* 结构简单,利于匿名内部类形式创建
* 缺点:
* 由于java是单继承的,这导致如果继承了线程就无法再继承其他类去复用方法.
* 创建线程的同时重写run方法将任务定义在线程中,这导致线程与任务存在必然的耦合关系,
* 不利于线程的重(chong)用。
*/
public class ThreadDemo {
public static void main(String[] args) {
MyThread myThread = new MyThread();
MyThread1 myThread1 = new MyThread1();
myThread.start();
myThread1.start();
}
}
class MyThread extends Thread{
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println("你是谁?");
}
}
}
class MyThread1 extends Thread{
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println("我是谁?");
}
}
}
实现Runnable接口单独定义线程任务
/*
* 第二种创建线程的方式:
* 实现Runnable接口单独定义线程任务
*/
public class ThreadDemo2 {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
MyRunnable1 mr1 = new MyRunnable1();
Thread t = new Thread(mr);
Thread t1 = new Thread(mr1);
t.start();
t1.start();
}
}
class MyRunnable implements Runnable{
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println("你是谁?");
}
}
}
class MyRunnable1 implements Runnable{
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println("我是谁?");
}
}
}
用匿名内部类完成线程的两种创建方式
/*
* 用匿名内部类完成线程的两种创建方式
*/
public class ThreadDemo3 {
public static void main(String[] args) {
/*
* 继承Thread重写run()方法
*/
Thread th = new Thread(()->{
for (int i = 0; i < 10; i++) {
System.out.println("你是谁?");
}
});
/*
* 实现Runnable接口重写run()方法
*/
Runnable runnable = ()->{
for (int i = 0; i < 10; i++) {
System.out.println("我是谁?");
}
};
Thread th1 = new Thread(runnable);
th.start();
th1.start();
}
}
Thread基本的应用
获取线程信息的相关方法
/*
* 获取线程信息的相关方法
*/
public class ThreadInfoDemo {
public static void main(String[] args) {
Thread main = Thread.currentThread(); //获取运行这个方法的线程
String name = main.getName(); //获取线程的名字
long id = main.getId(); //获取唯一标识
int priority = main.getPriority(); //获取优先级
boolean isAlive = main.isAlive(); //是否活着
boolean daemon = main.isDaemon(); //是否为守护线程
boolean interrupted = main.isInterrupted(); //是否被中断
System.out.println(name + " " + id + " " + priority + " " + isAlive + " " + daemon + " " + interrupted);
}
}
获取运行这个方法的线程
/*
* Java中所有代码都是靠线程运行的,main()方法也不例外,
* 执行main()方法的线程称为“主线程
* Thread提供了一个静态方法:
* static Thread currentThread()
* 这个方法可以获取运行这个方法的线程
* */
public class CurrentThreadDemo {
public static void main(String[] args) {
Thread main = Thread.currentThread();
System.out.println(main);
dosome();
}
public static void dosome(){
Thread thread = Thread.currentThread();
System.out.println("运行dosome方法的线程是: " + thread);
}
}
sleep阻塞
/*
* sleep阻塞:
* 线程提供了一个静态方法:
* static void sleep(long ms)
* 该方法可以让执行这个方法的线程进入阻塞状态(BLOCK状态)指定毫秒,
* 超时后线程会自动回到RUNNABLE状态再次开始并发执行
*/
public class SleepDemo {
public static void main(String[] args) {
System.out.println("程序开始");
/*
* 简易倒计时程序
*/
for (int i = 5; i > 0; i--) {
System.out.println(i);
try {
Thread.sleep(1000); //线程阻塞5秒
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("程序结束");
}
}
守护线程
守护线程是通过线程的方法setDaemon(boolean on) 传入参数true将一个普通线程设置而转变的
/*
* 守护线程:
* 守护线程是通过线程的方法setDaemon(boolean on) 传入参数true将一个普通线程设置而转变的
* 守护线程有一点是与普通线程不同的就是进程的结束。
* 当java所有的普通线程都退出时,Java进程就会退出,此时会杀死所有还在运行的守护线程
* 守护线程适合运行那些我们不关心结束时机的任务,只要主要任务都执行完毕它们可以跟着停下来时,
* 这些任务就可以跑在守护线程上。最有代表性的就是GC,它就是跑在一个守护线程上的。
*/
public class DaemonThreadDemo {
public static void main(String[] args) {
Thread rose = new Thread() {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("rose");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("跳");
}
};
Thread jack = new Thread(){
public void run(){
while (true){
System.out.println("jack");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
rose.start();
jack.setDaemon(true);
jack.start();
System.out.println("main方法结束,主线程结束了");
}
}
线程优先级
线程有10个优先级,分别用整数1-10表示,其中1最低,10最高,5是默认值
当一个线程调用start后便纳入到了线程调度器被统一管理。
线程只能被动的 被分配时间片 得以并发运行,不能主动索取时间片。
通过调整线程的优先级可以最大程度的改变获取时间片的概率。
在同一个CPU核心中并发执行的线程中优先级越高线程获取时间片的次数越多
/*
* 线程优先级:
* 线程有10个优先级,分别用整数1-10表示,其中1最低,10最高,5是默认值
* 当一个线程调用start后便纳入到了线程调度器被统一管理。
* 线程只能被动的 被分配时间片 得以并发运行,不能主动索取时间片。
* 通过调整线程的优先级可以最大程度的改变获取时间片的概率。
* 在同一个CPU核心中并发执行的线程中优先级越高线程获取时间片的次数越多
*/
public class PriorityDemo {
public static void main(String[] args) {
Thread min = new Thread(){
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println("min");
}
}
};
Thread norm = new Thread(){
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println("norm");
}
}
};
Thread max = new Thread(){
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println("max");
}
}
};
min.setPriority(Thread.MIN_PRIORITY); //设置线程优先级
norm.setPriority(6);
max.setPriority(Thread.MAX_PRIORITY);
min.start();
norm.start();
max.start();
}
}
多线程并发安全问题
当多个线程并发操作同一临界资源由于线程切换时机不确定,导致执行顺序出现混乱从而导致不良后果
临界资源: 操作该资源的完成过程同一时刻只能被单一线程进行的。
同步锁
同步方法
/*
* 多线程并发安全问题:
* 当多个线程并发操作同一临界资源由于线程切换时机不确定,
* 导致执行顺序出现混乱从而导致不良后果
* 临界资源:
* 操作该资源的完成过程同一时刻只能被单一线程进行的。
*/
public class SyncDemo {
public static void main(String[] args) {
Table table = new Table();
Thread thread = new Thread() {
@Override
public void run() {
while (true) {
int bean = table.getBeans();
Thread.yield();
System.out.println(getName() + ": " + bean);
}
}
};
Thread thread1 = new Thread() {
@Override
public void run() {
while (true) {
int bean = table.getBeans();
Thread.yield();
System.out.println(getName() + ": " + bean);
}
}
};
thread.start();
thread1.start();
}
}
class Table {
private int beans = 10;
/*
* 该方法上使用synchronized关键字后,该方法称为“同步方法”
* 多个线程不能同时在方法内部执行
* 将多个线程并发执行改为同步执行(有先后顺序)的执行可有效的解决并发安全问题
*/
public synchronized int getBeans() {
if (beans == 0) {
throw new RuntimeException("没有豆子了");
}
Thread.yield(); //该方法可以让运行该方法的线程主动放弃本次剩余时间
return beans--;
}
}
同步块
/*
* 同步块:
* 有效缩小同步范围可以在保证并发安全的前提下尽可能地提高并发效率。
* 同步块语法:
* synchronized(同步监视器对象){
* 需要多个线程同步执行的代码片段
* }
*/
public class SyncDemo2 {
public static void main(String[] args) {
Shop shop = new Shop();
Shop shop1 = new Shop();
Thread thread = new Thread("张三"){
@Override
public void run() {
shop.buy();
}
};
Thread thread1 = new Thread("李四"){
@Override
public void run() {
shop1.buy();
}
};
thread.start();
thread1.start();
}
}
class Shop {
public void buy() {
try {
Thread thread = Thread.currentThread();
System.out.println(thread.getName() + "挑选");
Thread.sleep(3000);
/*
* 同步块使用时要指定同步监视器对象,该对象的选取要同时满足:
* 必须是一个引用对象
* 多个需要同步执行该代码片段的线程看到的对象必须是同一个
* 合适地锁对象应该在存在并发安全问题时可以限制多个线程同步执行,
* 不存在并发安全问题时可以同时执行。
*/
synchronized (this) {
System.out.println(thread.getName() + "试选");
Thread.sleep(3000);
}
System.out.println("结账");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
同步监视器对象的选取原则
import java.util.ArrayList;
import java.util.List;
/*
* 同步监视器对象的选取原则:
* 通常选取临界资源即可。
*/
public class Test {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
Thread thread = new Thread(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (list) {
list.add(i);
}
}
}
};
Thread thread1 = new Thread(){
@Override
public void run() {
for (int i = 1000; i < 1010; i++) {
synchronized (list) {
list.add(i);
}
}
}
};
thread.start();
thread1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(list);
}
}
静态方法
/*
* 静态方法上如果使用了synchronized,那么该方法一定具有同步方法
*/
public class SyncDemo3 {
public static void main(String[] args) {
Foo foo = new Foo();
Foo foo1 = new Foo();
Thread thread = new Thread(){
@Override
public void run() {
foo.dosome();
}
};
Thread thread1 = new Thread(){
@Override
public void run() {
foo1.dosome();
}
};
thread.start();
thread1.start();
}
}
class Foo {
public synchronized static void dosome() {
try {
Thread thread = Thread.currentThread();
System.out.println(thread.getName() + "正在执行");
Thread.sleep(3000);
System.out.println(thread.getName() + "执行完成");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
互斥锁
/*
* 互斥锁:
* 当使用多个synchronized锁定多个代码片段,并且指定的同步监视器对象是同一个时,
* 那么这些代码片段之间就是互斥的,多个线程不能同时执行它们。
*/
public class SyncDemo4 {
public static void main(String[] args) {
Boo boo = new Boo();
Thread thread = new Thread(){
@Override
public void run() {
boo.mehtodA();
}
};
Thread thread1 = new Thread(){
@Override
public void run() {
boo.mehtodB();
}
};
thread.start();
thread1.start();
}
}
class Boo {
public synchronized void mehtodA() {
try {
Thread thread = Thread.currentThread();
System.out.println(thread.getName() + "A正在执行");
Thread.sleep(3000);
System.out.println(thread.getName() + "A执行完成");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public synchronized void mehtodB() {
try {
Thread thread = Thread.currentThread();
System.out.println(thread.getName() + "B正在执行");
Thread.sleep(3000);
System.out.println(thread.getName() + "B执行完成");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程池:
* 线程池是线程的管理类,主要解决两个问题:
* 1.控制线程的数量
* 2.重用线程
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
//创建一个线程池
ExecutorService threadPool = Executors.newFixedThreadPool(3); //2条线程
/**
* 指派任务
*/
for (int i = 0; i < 5; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
Thread thread = Thread.currentThread();
System.out.println(thread.getName() + "正在执行");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(thread.getName() + "执行完成");
}
};
threadPool.execute(runnable); //将任务交给线程池
System.out.println("将一个任务交给线程池");
}
threadPool.shutdown(); //不再接收新任务,将现有任务执行完成后结束线程池中所有线程
// threadPool.shutdownNow(); //不再接收新任务,中断现有任务执行并结束线程池中所有线程
System.out.println("关闭线程池");
}
}