读前说明:
由于是多线程编程,CPU调度线程的随机性,运行结果,每次都有可能不同,故在此没有截图程序的运行结果。读者可自行运行程序,检验结果。
本文着重介绍java多线程编程的基础---耐心看完,就会有满满的收获
多线程
1.如果程序只有一条执行路径(main),那么该程序就是单线程程序。
2.如果程序有多条执行路径,那么该程序就是多线程程序。
11.Java程序的运行原理:
由java命令启动JVM,JVM启动就相当于启动了一个进程(操作系统windows层面看),
接着由该进程创建一个主线程去调用main方法。
思考题:jvm虚拟机的启动是单线程的还是多线程的?
多线程
原因是:垃圾回收线程也要先启动,否则很容易出现内存溢出。
现在的垃圾回收线程加上前面的主线程,最少启动了两个线程,所以,jvm 的启动其实是多线程的。
12.java是不能直接调用系统功能的(需要使用C/C++语言)。
进程是由系统创建的,所以我们应该去调用系统功能创建一个进程(C/C++语言实现)
- 实现Runnable接口
多个人抢CPU的执行权去干自己的活。
多个人抢CPU的执行权去干共同的活。---线程安全问题
Synchronized关键字放在权限修饰符后。public synchronized void 方法名(){}
Collections 集合工具类(以下三个方法均为静态方法)
- synchronizedList
返回指定列表支持的同步(线程安全的)列表。 - synchronizedMap
返回由指定映射支持的同步(线程安全的)映射。 - synchronizedSet
返回指定 set 支持的同步(线程安全的)set。
线程安全的类StringBuffer、Vector、HashTable
public class MyThread {
public static final MyThread my1=new MyThread();
public static final MyThread my2=new MyThread();
}
public class DieLock extends Thread{
private boolean flag;
public DieLock(boolean flag) {
this.flag=flag;
}
@Override
public void run() {
while(true) {
if(flag) {
synchronized(MyThread.my1) {
System.out.println("if 1");
synchronized(MyThread.my2) {
System.out.println("if 2");
}
}
}else {
synchronized(MyThread.my2) {
System.out.println("else 1");
synchronized(MyThread.my1) {
System.out.println("else 2");
}
}
}
}
}
}
public class DieLockTest {
public static void main(String[] args) {
DieLock t1=new DieLock(true);
DieLock t2=new DieLock(false);
t1.start();
t2.start();
}
}
public class Student {
String name;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class SetStudent implements Runnable {
Student student;
public SetStudent(Student student) {
this.student = student;
}
@Override
public void run() {
student.setName("wxz");
student.setAge(20);
}
}
public class GetStudent implements Runnable {
Student student;
public GetStudent(Student student) {
this.student = student;
}
@Override
public void run() {
System.out.println(student.getName()+"---"+student.getAge());
}
}
public class StudentTest {
public static void main(String[] args) {
Student s1=new Student();
SetStudent s=new SetStudent(s1);
GetStudent g = new GetStudent(s1);
Thread t1=new Thread(s);
Thread t2=new Thread(g);
t2.start();
t1.start();
}
}
不同种类线程加的锁必须是同一把锁。即锁对象必须是同一个,本例中的共享资源(student对象就是同一个对象,因此可以作为锁对象)。
s.wait(); //t2就等待了。此时立即释放锁,将来是从这里醒过来的。
s.notify();//唤醒并不表示立马可以执行,必须还得抢CPU的执行权。
public class Student {
String name;
int age;
boolean flag;//默认值为false
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class SetStudent implements Runnable {
Student student;
private int x=0;
public SetStudent(Student student) {
this.student = student;
}
@Override
public void run() {
while(true){
synchronized(student) {
if(student.flag) {
try {
student.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(x % 2 == 0) {
student.setName("wxz");
student.setAge(20);
//System.out.println("生产");
}else {
student.setName("summer");
student.setAge(10);
//System.out.println("生产");
}
x++;
student.flag=true;
student.notify();//唤醒并不表示立马可以执行,必须还得抢CPU的执行权
}
}
}
}
flag标志的作用:表示是否有生产的资源, 若有便可以通知消费者消费。
若没有便可通知生产者生产。
public class GetStudent implements Runnable {
Student student;
public GetStudent(Student student) {
this.student = student;
}
@Override
public void run() {
while(true){
synchronized(student) {
if(!student.flag) {
try {
student.wait();//t2等待,立即释放锁,将来是从这里醒过来的
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//System.out.println("消费");
System.out.println(student.getName()+"---"+student.getAge());
student.flag=false;
student.notify();
}
}
}
}
public class StudentTest {
public static void main(String[] args) {
Student s1=new Student();
SetStudent s=new SetStudent(s1);
GetStudent g = new GetStudent(s1);
Thread t1=new Thread(s);
Thread t2=new Thread(g);
t2.start();
t1.start();
}
}
生产者与消费者问题的最终版代码
把Student的成员变量给私有化,
把设置和获取的操作封装成了功能,并加了同步。
设置或者获取的线程里面之需要调用方法即可。
package com.study;
public class Student {
private String name;
private int age;
private boolean flag;//默认值为false
// 方法锁对象为默认的this
// 那个对象调用set方法,那么this指得就是那个对象。由于这里的set和get方法都是由student对象来调用的,
// 故这里的this指的是student对象,因此锁对象一样。能解决线程安全问题。
public synchronized void set(String name,int age) {
if(this.flag) {
//System.out.println(this+"=========================================");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name=name;
this.age=age;
this.flag=true;
this.notify();
}
public synchronized void get() {
if(!this.flag) {
//System.out.println(this+"+++++++++++++++++++++++++++++++++++++++++");
try {
this.wait();
} catch (InterruptedException e) {
}
}
System.out.println(this.name+"---"+this.age);
this.flag=false;
this.notify();
}
}
package com.study;
public class SetStudent implements Runnable {
Student student;
private int x=0;
public SetStudent(Student student) {
this.student = student;
}
@Override
public void run() {
while(true){
if(x%2==0) {
student.set("wxz",10);
}else {
student.set("summer",20);
}
x++;
}
}
}
package com.study;
public class GetStudent implements Runnable {
Student student;
public GetStudent(Student student) {
this.student = student;
}
@Override
public void run() {
while(true){
student.get();
}
}
}
package com.study;
public class StudentTest {
public static void main(String[] args) {
Student s1=new Student();
SetStudent s=new SetStudent(s1);
GetStudent g = new GetStudent(s1);
Thread t1=new Thread(s);
Thread t2=new Thread(g);
t2.start();
t1.start();
}
}
线程池
package com.executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 线程池有五个线程,来了六个任务,只能等一个线程干完,还了链接之后才可以开始最后一个任务。
public class ExecutorsTest {
public static void main(String[] args) {
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);
newFixedThreadPool.submit(new Task());
newFixedThreadPool.submit(new Task());
newFixedThreadPool.submit(new Task());
newFixedThreadPool.submit(new Task());
newFixedThreadPool.submit(new Task());
newFixedThreadPool.submit(new Task());
newFixedThreadPool.shutdown();//关闭线程池
}
}
package com.executors;
public class Task implements Runnable {
@Override
public void run() {
for (int i = 0;i <10 ;i++) {
System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
定时器
package com.timer;
import java.util.Timer;
public class TimerTest {
public static void main(String[] args) {
Timer timer=new Timer();
timer.schedule(new MyTask(timer), 5000);
}
}
package com.timer;
import java.util.Timer;
import java.util.TimerTask;
public class MyTask extends TimerTask{
private Timer timer;
public MyTask() {
}
public MyTask(Timer timer) {
this.timer=timer;
}
@Override
public void run() {
System.out.println("到点了");
timer.cancel();
}
}
package com.timer;
import java.util.Timer;
public class TimerTest {
public static void main(String[] args) {
Timer timer=new Timer();
timer.schedule(new MyTask(), 1000,1000);
}
}
package com.timer;
import java.util.Date;
import java.util.TimerTask;
public class MyTask extends TimerTask{
@Override
public void run() {
System.out.println(new Date());
}
}
定时删除指定目录下的资源:
package com.timer;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
public class DelectTest {
public static void main(String[] args) throws ParseException {
Timer timer=new Timer();
String date="2020-12-18 16:48:00";
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date time=simpleDateFormat.parse(date);
timer.schedule(new DeleteFolder(), time);
}
}
package com.timer;
import java.io.File;
import java.util.TimerTask;
public class DeleteFolder extends TimerTask{
@Override
public void run() {
File file = new File("E:/test");
deleteFolder(file);
}
private void deleteFolder(File file) {
if(file != null) {
File[] listFiles = file.listFiles();
for(File f:listFiles) {
if (f.isDirectory()) {
deleteFolder(f);
}else {
System.out.println(f.getName());
f.delete();
}
}
file.delete();
}
}
}