Java多线程基础(1)
学习笔记为手打,教程为 【狂神说Java】多线程详解
线程基础
1. 线程就是独立执行路径
2. 在程序运行时,及时没有自己创建线程,后台夜会有多个线程,如主线程,gc线程
3. main()称之为主线程,为系统的入口,用于执行整个程序
4. 在一个进程中,如果开辟多个线程,线程的运作有调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的
5. 对同一份资源操作时,会存在资源抢夺问题,需要加入并发控制
6. 线程会带来额外的开销,如cpu调度时间,并发控制开销
7. 每个线程在自己的工作内存交互,内存控制不当会造成数据的不一致
线程创建
三种创建方式
Thread class
子类继承Thread具备多线程功能
启动线程:子类对象.start()
OOP单继承局限性
public class ThreadsDemo2 extends Thread{
private String url; /*网络图片位置*/
private String name; /*文件名称*/
public ThreadsDemo2(String url, String name) {
this.url = url;
this.name = name;
}
//下载图片的执行体
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
try {
webDownloader.downloader(url,name);
System.out.println("下载图片为"+ name);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
String url1 = "https://img0.baidu.com/it/u=4156074303,275806129&fm=26&fmt=auto&gp=0.jpg";
String url2 = "https://img1.baidu.com/it/u=1684000154,1654099979&fm=26&fmt=auto&gp=0.jpg";
String url3 = "https://img0.baidu.com/it/u=1506294494,3075748085&fm=26&fmt=auto&gp=0.jpg";
String name1 = "头像1.jpg";
String name2 = "头像2.jpg";
String name3 = "头像3.jpg";
ThreadsDemo2 t1= new ThreadsDemo2(url1,name1);
ThreadsDemo2 t2= new ThreadsDemo2(url1,name2);
ThreadsDemo2 t3= new ThreadsDemo2(url1,name3);
t1.start();
t2.start();
t3.start();
}
}
/**
* 下载器
*/
class WebDownloader {
public void downloader(String url, String name) throws IOException {
FileUtils.copyURLToFile(new URL(url), new File(name));
}
}
第一次执行
第二次执行
结论:两次执行的次序并不按顺序,可知三个线程是并行的。
Runnable interface
实现Runnable具有多线程能力
启动线程:传入目标对象+thread对象.start()
灵活,方便对象被多个线程使用
public class ThreadsDemo3 implements Runnable{
@Override
public void run() {
//run()方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("========run"+i);
}
}
public static void main(String[] args) {
ThreadsDemo3 t1 = new ThreadsDemo3();
new Thread(t1).start();
for (int i = 0; i < 2000; i++) {
System.out.println("======main"+i);
}
}
}
Callable interface (暂时了解 21/08/23)
public class ThreadsDemo6 implements Callable<Boolean> {
private String url; /*网络图片位置*/
private String name; /*文件名称*/
public ThreadsDemo6(String url1, String name1) {
this.url = url1;
this.name = name1;
}
@Override
public Boolean call() throws Exception {
WebDownloader webDownloader = new WebDownloader();
try {
webDownloader.downloader(url,name);
System.out.println("下载图片为"+ name);
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
String url1 = "https://img0.baidu.com/it/u=4156074303,275806129&fm=26&fmt=auto&gp=0.jpg";
String url2 = "https://img1.baidu.com/it/u=1684000154,1654099979&fm=26&fmt=auto&gp=0.jpg";
String url3 = "https://img0.baidu.com/it/u=1506294494,3075748085&fm=26&fmt=auto&gp=0.jpg";
String name1 = "头像1.jpg";
String name2 = "头像2.jpg";
String name3 = "头像3.jpg";
ThreadsDemo6 t1= new ThreadsDemo6(url1,name1);
ThreadsDemo6 t2= new ThreadsDemo6(url1,name2);
ThreadsDemo6 t3= new ThreadsDemo6(url1,name3);
//创建执行服务
ExecutorService executorService= Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 =executorService.submit(t1);
Future<Boolean> r2 =executorService.submit(t2);
Future<Boolean> r3 =executorService.submit(t3);
//获取结果
Boolean rs1 = r1.get();
Boolean rs2 = r2.get();
Boolean rs3 = r3.get();
//关闭服务
executorService.shutdownNow();
}
}
线程并发
发现问题
多个线程操作同一个资源识,线程不安全,数据紊乱
public class ThreadsDemo4 implements Runnable{
private int ticketNum = 10;
@Override
public void run() {
while (true){
if (ticketNum <1 ) {
break;
}
System.out.println( Thread.currentThread().getName()+ "===>拿了第"+ticketNum-- +"票");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ThreadsDemo4 ticket = new ThreadsDemo4();
new Thread(ticket, "XiaoMing").start();
new Thread(ticket, "XiaoHong").start();
new Thread(ticket, "HuangNiu").start();
}
}
加深对多线程的印象——模拟龟兔赛跑
public class ThreadsDemo5 implements Runnable {
//胜利者
private static String winner;
//跑道长度
private static int RUNWAY_LENGTH = 50;
//游戏结束标志
boolean gameOver(int i){
//胜者唯一
if (winner!=null){
return true;
}
if( i >= RUNWAY_LENGTH){
winner = Thread.currentThread().getName();
System.out.println("===比赛结束==="+"\n"+winner+"-->获得了胜利");
return true;
}
return false;
}
@Override
public void run() {
for (int i = 0; i <= RUNWAY_LENGTH; i++) {
boolean flag = gameOver(i);
if (flag) {
break;
}
String name = Thread.currentThread().getName();
System.out.println(name + "-->已经跑了" + i + "米");
//兔子跑的快
if ("兔子".equals(name)){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//乌龟爬的慢
if ("乌龟".equals(name)) {
System.out.println("加油!!!!");
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//兔子半路睡着
if ("兔子".equals(name) && i == RUNWAY_LENGTH/2){
System.out.println("怎么能睡觉呢?\n");
try {
Thread.sleep(60);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
ThreadsDemo5 race = new ThreadsDemo5();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}