线程的基本概念
一个程序中可以有多条同时执行的线索,一个线程就是程序中执行的一条线索,每个线程都关联有需要执行的代码,即程序可以同时执行多段代码块,每个程序至少有一个线程,就是main方法执行的主线程。
如果是单核的cpu如何同时执行多段程序呢?
其实cpu是一会执行a线索,一会执行b线索,切换的时间非常短,所以给人感觉是a,b线索同时执行
线程的状态:
就绪、运行、阻塞、挂起、结束
调用线程的start方法后,线程进入就绪状态,线程调度系统会将就绪状态转变为运行状态。
当遇到synchronized语句时,线程会进入阻塞状态,当获得锁后,会从阻塞状态变为运行状态。
当线程在synchronized阻塞时,可以调用wait方法,使其变为挂起状态
直接调用sleep方法也会进入挂起状态(wait与sleep的区别:https://blog.youkuaiyun.com/weixin_43864073/article/details/87826427
线程中的代码执行完之后就变为结束状态
线程同步与异步:
如果数据在线程中共享,如一个线程正在写的数据可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,就需要使用线程同步存取。
当应用程序在对象上调用了一个需要很长时间执行的方法,并且程序并不需要得到这个方法的返回值
,此时应该采用异步编程,也就是使用多线程
实现线程的三种方法:
1.继承Thread类
public class MyThread extends Thread {
@Override
public void run() {
System.out.println(“MyThread”);
}
public static void main(String [] args){
MyThread myThread =new MyThread();
myThread.start();
}
}
2.实现Runnable接口
public class MyThread implements Runnable {
@Override
public void run() {
System.out.println("MyThread");
}
public static void main(String [] args){
Thread thread=new Thread(new MyThread());
thread.start();
}
}
3.使用线程池
public class MyThread {
public static void main(String [] args){
ExecutorService pool=Executors.newFixedThreadPool(1);
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println(“MyThread”);
}
});
pool.shutdown();
}
}
synchronized与Lock的异同
相同点:
synchronized能做的事Lock都能做。。。
不同点:
Lock具有更好的性能及更精确的线程语义,synchronized会自动释放锁,Lock则要求程序员手动释放锁,并且必须在finally语句中释放
附带一个线程题
子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100,如此循环50次,请写出程序。
先实现一轮的子线程循环10次,接着主线程循环100
public class ThreadTest {
//这个用于判断哪个线程该执行
private static boolean isShouldMain = false;
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
//用字节码对象当锁,因为它是唯一的
synchronized (ThreadTest.class) {
//先判断是否是主线程执行,是的话,把子线程挂起
if(isShouldMain){
try {
ThreadTest.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//当循环次数到10次时,把isShouldMain赋值为true,并唤醒主线程
if(i==10){
isShouldMain=true;
ThreadTest.class.notify();
}
System.out.println("子线程:" + i);
}
}
}
}).start();
for (int i = 1; i <= 100; i++) {
synchronized (ThreadTest.class) {
if(!isShouldMain){
try {
ThreadTest.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//当循环次数到100次时,把isShouldMain赋值为false,并唤醒子线程
if(i==100){
isShouldMain=false;
ThreadTest.class.notify();
}
System.out.println("主线程:" + i);
}
}
}
}
50次循环的版本,道理是一样的,只是重复50次
public class ThreadTest {
private static boolean isShouldMain = false;
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
//加了50次循环
for (int j=1;j<=50;j++){
for (int i = 1; i <= 10; i++) {
synchronized (ThreadTest.class) {
if(isShouldMain){
try {
ThreadTest.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(i==10){
isShouldMain=true;
ThreadTest.class.notify();
}
System.out.println("循环次数:"+j+",子线程:" + i);
}
}
}
}
}).start();
for (int j=1;j<=50;j++){
for (int i = 1; i <= 100; i++) {
synchronized (ThreadTest.class) {
if(!isShouldMain){
try {
ThreadTest.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(i==100){
isShouldMain=false;
ThreadTest.class.notify();
}
System.out.println("循环次数:"+j+",主线程:" + i);
}
}
}
}
}