多线程相关
多线程
一、进程与线程
-
进程:执行程序的一次执行过程,是系统资源分配的单位。
-
线程:一个进程可以包含若干个线程,线程是CPU调度和执行的单位。
注意:在一个CPU的情况下,在同一时间点,CPU只能执行一个代码,因为切换的很快,所以就有了同时执行的错觉,只有在多CPU(即多核)的前提下才能实现真正的多线程。
二、线程的创建
线程创建的三种方式
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
1. 继承Thread类
package zander.example.thread;
/**
* 通过集成继承Thread类的方式创建线程
* 1.继承Thread类
* 2.重写run方法
* 3.调用start()方法开启线程
*/
public class TestThread1 extends Thread{
@Override
public void run() {//run方法线程体
for(int i = 0; i < 20; i++){
System.out.println("我的第"+(i+1)+"run");
}
}
public static void main(String[] args) {//main主线程
//创建一个线程对象
TestThread1 testThread = new TestThread1();
//调用start方法开启线程
testThread.start();
for(int i = 0 ; i <500; i++) {
System.out.println("main"+(i+1));
}
}
}
package zander.example.thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/**
* 通过线程下载图片
*/
public class TestThread2 extends Thread{
private String url ;
private String name;
public TestThread2(String url , String name) {
this.url = url;
this.name = name;
}
@Override
public void run() {
FileDownload fileDownload = new FileDownload();
fileDownload.download(url, name);
System.out.println(name);
}
public static void main(String[] args) {
TestThread2 test1
= new TestThread2("https://img2.baidu.com/it/u=3599223828,1689461421&fm=26&fmt=auto&gp=0.jpg","1.jpg");
TestThread2 test2
= new TestThread2("https://img2.baidu.com/it/u=3599223828,1689461421&fm=26&fmt=auto&gp=0.jpg", "2.jpg");
TestThread2 test3
= new TestThread2("https://img2.baidu.com/it/u=3599223828,1689461421&fm=26&fmt=auto&gp=0.jpg", "3.jgp");
test1.start();
test2.start();
test3.start();
}
}
class FileDownload {
public void download(String url , String name) {
try {
//commons-io包提供的FileUtils工具类,根据链接下载
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("下载文件FileDownload类的down方法出错!");
}
}
}
2. 实现Runnable接口
package zander.example.runnable;
/**
* 通过实现Runnable接口的方式创建线程
* 1.实现Runnable接口
* 2.重写run方法
* 3.以参数形式丢入runnable接口的实现类,调用start方法
*/
public class TestRunnable1 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我的第"+(i+1)+"个run");
}
}
public static void main(String[] args) {
//创建Runnable接口的实现类对象
TestRunnable1 testRunnable1 = new TestRunnable1();
//创建线程对象,通过线程对象开启线程(代理)
new Thread(testRunnable1).start();
for(int i = 0 ; i <500; i++) {
System.out.println("main"+(i+1));
}
}
}
package zander.example.runnable;
import org.apache.commons.io.FileUtils;
import zander.example.thread.TestThread2;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/**
* 通过线程下载图片
*/
public class TestRunnable2 implements Runnable{
private String url;
private String name;
public TestRunnable2(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public void run() {
FileDownload fileDownload = new FileDownload();
fileDownload.download(url, name);
System.out.println("下载了"+name+"文件");
}
public static void main(String[] args) {
TestRunnable2 runnable1
= new TestRunnable2("https://img2.baidu.com/it/u=3599223828,1689461421&fm=26&fmt=auto&gp=0.jpg", "1.jpg");
TestRunnable2 runnable2
= new TestRunnable2("https://img2.baidu.com/it/u=3599223828,1689461421&fm=26&fmt=auto&gp=0.jpg", "2.jpg");
TestRunnable2 runnable3
= new TestRunnable2("https://img2.baidu.com/it/u=3599223828,1689461421&fm=26&fmt=auto&gp=0.jpg", "3.jpg");
new Thread(runnable1).start();
new Thread(runnable2).start();
new Thread(runnable3).start();
}
}
class FileDownload {
public void download(String url , String name) {
try {
//commons-io包提供的FileUtils工具类,根据链接下载
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("下载文件FileDownload类的down方法出错!");
}
}
}
3.实现Callable接口
package zander.example.callable;
import java.util.concurrent.*;
/**
* 通过实现Callable接口的方式创建线程
* 1.实现Callable接口
* 2.重写call方法,带返回值
* 3.创建执行服务 ExecutorService executorService = Executors.newFixedThreadPool(3);
* 4.提交执行 Future<Boolean> future = executorService.submit(testCallable1);
* 5.获取执行结果 boolean result = future.get();
* 6.关闭服务 executorService.shutdownNow();
*/
public class TestCallable1 implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
for (int i = 0; i < 20; i++) {
System.out.println("我的第" + (i + 1) + "个call");
}
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable1 testCallable1 = new TestCallable1();
//创建执行服务
ExecutorService executorService = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> future = executorService.submit(testCallable1);
//获取结果
boolean result = future.get();
//关闭服务
executorService.shutdownNow();
}
}
线程状态
三、线程停止
package zander.example.runnable;
/**
* 线程停止
* 尽量不要使用jdk已经不建议使用的方式,如stop()、destroy()等
* 可以使用标志位的方式,让线程自己停止
*/
public class TestStop implements Runnable {
private static boolean flag = true;
@Override
public void run() {
while (flag) {
System.out.println("线程" + Thread.currentThread().getName() + "正在运行");
}
}
//设置一个停止线程的方法,转换标志位
public void stop() {
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main方法正在执行--" + i);
if (i == 900) {
testStop.stop();
System.out.println("停止线程" + i);
}
}
}
}
四、线程休眠
package zander.example.runnable;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 线程休眠
*/
public class TestSleep{
public static void main(String[] args) {
Date start = new Date(System.currentTimeMillis()); //获取当前时间
while (true) {
try {
System.out.println(new SimpleDateFormat("HH:mm:ss").format(start));
Thread.sleep(1000);
start = new Date(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println();
}
}
}
五、线程礼让
package zander.example.runnable;
/**
* 线程礼让
* 线程礼让后,重新由CPU调度,所以礼让不一定成功
*/
public class TestYield {
public static void main(String[] args) {
Yield yield = new Yield();
new Thread(yield, "线程1").start();
new Thread(yield, "线程2").start();
}
}
class Yield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"结束执行");
}
}
六、线程强制执行
package zander.example.runnable;
import jdk.nashorn.internal.scripts.JO;
/**
* 线程强制执行
*/
public class TestJoin {
public static void main(String[] args) throws InterruptedException {
Join join = new Join();
Thread thread = new Thread(join);
thread.start();
for (int i = 0; i < 1000; i++) {
if (i == 50) {
thread.join();
}
System.out.println("main"+i);
}
}
}
class Join implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("join" + i);
}
}
}
七、观察线程状态
package zander.example.runnable;
/**
* 观察线程状态
*/
public class WatchThreadState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
try {
Thread.sleep(1000); //休眠的时候 TIMED_WAITING状态
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程结束"); // 结束后TERMINATED状态
});
Thread.State state = thread.getState();
System.out.println(state); //NEW 状态
thread.start(); //RUNNABLE 状态
state = thread.getState();
System.out.println(state);
while (state != Thread.State.TERMINATED){
Thread.sleep(100);
state = thread.getState();
System.out.println(state);
}
}
}
八、线程优先级
package zander.example.runnable;
/**
* 设置线程优先级
* 优先级高的只是权重高,但是不一定先执行,最终还是需要看CPU调度
* setPriority 方法 优先级 1~10
*/
public class TestPriority {
public static void main(String[] args) {
Priority priority = new Priority();
Thread thread1 = new Thread(priority, "线程1");
Thread thread2 = new Thread(priority, "线程2");
Thread thread3 = new Thread(priority, "线程3");
Thread thread4 = new Thread(priority, "线程4");
Thread thread5 = new Thread(priority, "线程5");
Thread thread6 = new Thread(priority, "线程6");
Thread thread7 = new Thread(priority, "线程7");
Thread thread8 = new Thread(priority, "线程8");
thread1.setPriority(3);
thread1.start();
thread2.setPriority(8);
thread2.start();
thread3.setPriority(2);
thread3.start();
thread4.setPriority(10);
thread4.start();
thread5.setPriority(6);
thread5.start();
thread6.setPriority(7);
thread6.start();
thread7.setPriority(1);
thread7.start();
thread8.setPriority(9);
thread8.start();
}
}
class Priority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行,它的优先级是"+Thread.currentThread().getPriority());
}
}
九、守护线程
package zander.example.runnable;
/**
* 守护线程
* 1.线程分为用户线程和守护线程
* 2.虚拟机必须保证用户线程执行完毕,但不用等待守护线程执行完毕
*/
public class TestDaemon {
public static void main(String[] args) {
UserThread userThread = new UserThread();
Daemon daemon = new Daemon();
Thread uThread = new Thread(userThread);
Thread dThread = new Thread(daemon);
dThread.setDaemon(true); //将daemon设置为守护线程
uThread.start();
dThread.start();
}
}
class UserThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 365; i++) {
System.out.println(i+1);
}
System.out.println("end");
}
}
class Daemon implements Runnable{
@Override
public void run() {
int i = 0;
while (true) {
System.out.println("daemon"+(++i));
}
}
}
十、线程同步
- 多线程操作同一个对象(并发),线程不安全
package zander.example.runnable;
/**
* 模拟多个线程同时操作一个对象
*
* 注意:多个线程操作同一个资源的情况下,线程不安全
*/
public class TestRunnable3 implements Runnable {
private int num = 10;
@Override
public void run() {
while (true) {
if (num <= 0) {
break;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买了第" + (num--) + "张票");
}
}
public static void main(String[] args) {
TestRunnable3 testRunnable3 = new TestRunnable3();
new Thread(testRunnable3,"张三").start();
new Thread(testRunnable3,"李四").start();
new Thread(testRunnable3,"王五").start();
}
}
输出结果如下,可以发现线程不安全
王五获得了第9张票
王五获得了第8张票
王五获得了第7张票
王五获得了第6张票
王五获得了第5张票
王五获得了第4张票
王五获得了第3张票
张三获得了第10张票
张三获得了第2张票
张三获得了第1张票
李四获得了第10张票
- ArrayList线程不安全, CopyOnWriteArrayList线程安全
package zander.example.runnable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 测试线程不安全List
* 发现实际list的长度不足10000,因为并发导致往同一个位置插数的时候有覆盖
* 通过synchronized修饰方法,或者通过 synchronized (obj) {}代码块实现同步
*/
public class TestUnsafeList {
public static void main(String[] args) throws InterruptedException {
List unsafeList = new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
unsafeList.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(3000);//防止线程没有执行完成就打印
System.out.println(unsafeList.size()); //ArrayList线程不安全
/**
* ArrayList线程不安全,通过synchronized (unsafeList1)变为线程安全
*/
List unsafeList1 = new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
synchronized (unsafeList1){
unsafeList1.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(3000);
System.out.println(unsafeList1.size());
List safeList = new CopyOnWriteArrayList();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
safeList.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(3000);//防止线程没有执行完成就打印
System.out.println(safeList.size());//CopyOnWriteArrayList线程安全
}
}
十一、死锁
产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用
- 请求和保持条件
package zander.example.runnable;
/**
* 多个对象相互持有对方的锁
*/
public class TesteDeadLock {
public static void main(String[] args) {
C c1 = new C(0, "test1");
C c2 = new C(1, "test2");
new Thread(c1).start();
new Thread(c2).start();
}
}
class A {
}
class B {
}
class C implements Runnable{
static A a = new A();
static B b = new B();
int m;
String name;
public C(int m, String name) {
this.m = m;
this.name = name;
}
@Override
public void run() {
testDeadLock(m, name);
}
void testDeadLock(int m, String name){
if(m == 0 ){
synchronized (a){
System.out.println("AA");
synchronized (b){
System.out.println("BB");
}
}
}else{
synchronized (b){
System.out.println("BB");
synchronized (a) {
System.out.println("AA");
}
}
}
}
}
十二、Lock锁
package zander.example.runnable;
import java.util.concurrent.locks.ReentrantLock;
/**
* Lock锁
* ReentrantLock 可重入锁
* 使用lock.lock()加锁, lock.unlock()解锁。
*/
public class TestLock implements Runnable {
private int num = 10;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
//模拟延时
try {
if (num <= 0) {
break;
}
lock.lock(); //加锁
Thread.sleep(200);
System.out.println(Thread.currentThread().getName() + "获得了第" + (num--) + "张票");
} catch (InterruptedException e) {
lock.unlock(); //解锁
}
}
}
public static void main(String[] args) {
TestLock testRunnable3 = new TestLock();
new Thread(testRunnable3,"张三").start();
new Thread(testRunnable3,"李四").start();
new Thread(testRunnable3,"王五").start();
}
}
十三、线程协作
package zander.example.runnable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程池的创建
*/
public class TestPool {
public static void main(String[] args) {
Pool pool = new Pool();
//创建线程池
//newFixedThreadPool参数为线程池大小
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(pool);
executorService.execute(pool);
executorService.execute(pool);
executorService.execute(pool);
executorService.shutdownNow();
}
}
class Pool implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}

浙公网安备 33010602011771号